Overview

This notebook evaluates the behavioural predictions (RT, accuracy) that follow from the predicted rates of forgetting. We simulate the behavioural predictions that the adaptive fact learning system would have made, if it had used the predicted rate of forgetting as the starting estimate in the learning sequence.

Setup

library(fst)
fst package v0.9.8
library(data.table)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
data.table 1.14.8 using 1 threads (see ?getDTthreads).  Latest news: r-datatable.com
**********
This installation of data.table has not detected OpenMP support. It should still work but in single-threaded mode.
This is a Mac. Please read https://mac.r-project.org/openmp/. Please engage with Apple and ask them for support. Check r-datatable.com for updates, and our Mac instructions here: https://github.com/Rdatatable/data.table/wiki/Installation. After several years of many reports of installation problems on Mac, it's time to gingerly point out that there have been no similar problems on Windows or Linux.
**********
library(tidyr)
library(purrr)

Attaching package: ‘purrr’

The following object is masked from ‘package:data.table’:

    transpose
library(furrr)
Loading required package: future
library(stringr)
library(ggplot2)
library(patchwork)
library(wesanderson)
library(lme4)
Loading required package: Matrix

Attaching package: ‘Matrix’

The following objects are masked from ‘package:tidyr’:

    expand, pack, unpack
library(lmerTest)

Attaching package: ‘lmerTest’

The following object is masked from ‘package:lme4’:

    lmer

The following object is masked from ‘package:stats’:

    step
library(multcomp)
Loading required package: mvtnorm
Loading required package: survival

Attaching package: ‘survival’

The following object is masked from ‘package:future’:

    cluster

Loading required package: TH.data
Loading required package: MASS

Attaching package: ‘MASS’

The following object is masked from ‘package:patchwork’:

    area


Attaching package: ‘TH.data’

The following object is masked from ‘package:MASS’:

    geyser
library(emmeans)
source(file.path("..", "scripts", "99_slimstampen_model_funs.R"))

Attaching package: ‘dplyr’

The following object is masked from ‘package:MASS’:

    select

The following objects are masked from ‘package:data.table’:

    between, first, last

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
future::plan("multisession", workers = 9) # Set to desired number of cores
theme_set(theme_light(base_size = 14) +
            theme(strip.text = element_text(colour = "black")))

condition_colours <- wes_palette("Darjeeling1", n = 5)
condition_colours[c(2, 4, 5)] <- condition_colours[c(4, 5, 2)]

dataset_colours <- wes_palette("Darjeeling2", n = 5)[c(2, 3)]

Helper functions

load_data_with_predictions <- function (course) {
  
  # Data
  d_full <- read_fst(file.path("..", "data", paste0("formatted_", str_replace_all(course, " ", "_"), ".fst")))
  setDT(d_full)
  d <- d_full[!is.na(fact_id), .(user_id, fact_id, start_time, rt, correct)]
  rm(d_full)
  gc()
  setorder(d, user_id, fact_id, start_time)
  
  # ROF predictions
  pred_user <- read_fst(file.path("..", "data", "predictions", paste0("pred_v_obs_user_", str_replace_all(course, " ", "_"), ".fst")))
  pred_fact <- read_fst(file.path("..", "data", "predictions", paste0("pred_v_obs_fact_", str_replace_all(course, " ", "_"), ".fst")))
  pred_fact_user <- read_fst(file.path("..", "data", "predictions", paste0("pred_fact_and_user_", str_replace_all(course, " ", "_"), ".fst")))
  setDT(pred_user)
  setDT(pred_fact)
  setDT(pred_fact_user)
  
  # Remove NA and duplicates
  pred_user <- unique(pred_user[!is.na(alpha)])
  pred_fact <- unique(pred_fact[!is.na(alpha)])
  pred_fact_user <- unique(pred_fact_user[!is.na(alpha)])
  
  # Make Domain prediction
  pred_domain <- mean(unique(pred_fact, by = c("fact_id"))$pred_fact)
  pred_default <- 0.3
  
  # Combine
  setnames(pred_user, "n_train_obs", "n_train_obs_user")
  setnames(pred_fact, "n_train_obs", "n_train_obs_fact")
  pred_all <- merge(pred_user, pred_fact, by = c("user_id", "fact_id", "alpha", "n_reps"), all = TRUE)
  pred_all <- merge(pred_all, pred_fact_user, by = c("user_id", "fact_id", "alpha"), all = TRUE)
  pred_all[, pred_default := pred_default]
  pred_all[, pred_domain := pred_domain]

  d_prep <- d[, .(user_id,
                  fact_id,
                  text = "",
                  start_time,
                  rt,
                  correct,
                  threshold = -0.8)]
  
  d_pred <- merge(pred_all, d_prep, by = c("user_id", "fact_id"))
  
  return(d_pred)
}

predict_behaviour <- function (d) {
  
    # Process the data in manageable chunks
    chunk_size <- 1e4
    obs <- unique(d[, .(user_id, fact_id)])
    obs_chunks <- c(seq(1, nrow(obs), by = chunk_size), nrow(obs)+1)
    
    pred_beh <- map_dfr(1:(length(obs_chunks)-1), function (i) {
      
      msg <- paste("Chunk", i, "/", length(obs_chunks)-1)
      system(paste("echo", msg))
      
      obs_i <- obs[obs_chunks[i]:obs_chunks[i+1]-1]
      d_i <- d[obs_i, on = .(user_id, fact_id)]
      d_i_list <- split(d_i, by = c("user_id", "fact_id"), drop = TRUE)
      
      pred_beh_i <- future_map_dfr(d_i_list, function (learn_seq) {
        
        # Organise predictions for this sequence
        learn_seq_preds <- pivot_longer(
          learn_seq[1,], 
          pred_user:pred_domain,
          names_to = "prediction_type",
          names_prefix = "pred_",
          values_to = "predicted_alpha"
        )
        setDT(learn_seq_preds)
        
        # Look only at trial 3 in each sequence
        trial <- learn_seq[3,]
        delay <- learn_seq[2:3, diff(start_time)]
        
        # Calculate behavioural predictions for each prediction method
        map_dfr(seq(nrow(learn_seq_preds)), function (j) {
          
          prediction_type <- learn_seq_preds[j, prediction_type]
          predicted_alpha <- learn_seq_preds[j, predicted_alpha]
    
          predicted_activation <- NA
          predicted_accuracy <- NA
          predicted_rt <- NA
    
          if (!is.na(predicted_alpha)) {
            
            predicted_activation <- calculate_activation(
              time = trial[, start_time],
              id = trial[, fact_id],
              factalpha = predicted_alpha,
              responses = learn_seq[1:2,]
            )
      
            predicted_accuracy <- p_recall(
              activation = predicted_activation,
              threshold = -0.8,
              # activation_noise = 0.5
              activation_noise = 0.2
            )
            
            predicted_rt <- estimate_reaction_time_from_activation(
              activation = predicted_activation,
              reading_time = 300
            )
          }
          
          return(
            list(
              user_id = trial[, user_id],
              fact_id = trial[, fact_id],
              delay = delay,
              correct = trial[, correct],
              rt = trial[, rt],
              prediction_type = prediction_type,
              predicted_alpha = predicted_alpha,
              predicted_activation = predicted_activation,
              predicted_accuracy = predicted_accuracy,
              predicted_rt = predicted_rt
            )
          )
          
        })
      })
    })
    
    setDT(pred_beh)
    
    # Remove rows without prediction
    pred_beh <- pred_beh[!is.na(predicted_alpha)]
    pred_beh <- pred_beh[!is.infinite(predicted_rt)]
    
    # Set proper condition labels
    condition_labels <- data.table(
      prediction_type = c("default", "domain", "fact", "user", "fact_user"),
      prediction_label = factor(
        c("Default", "Domain", "Fact", "Learner", "Fact & Learner"),
        levels = c("Default", "Domain", "Fact", "Learner", "Fact & Learner")
      )
    )
    pred_beh <- pred_beh[condition_labels, on = .(prediction_type)]
    
    
    return(pred_beh)
}

Calculate predictions

Calculate behavioural predictions for trial 3:

pred_gl_beh_path <- file.path("..", "data", "predictions", "pred_behaviour_gl.fst")
pred_ss_beh_path <- file.path("..", "data", "predictions", "pred_behaviour_ss.fst")

if (!file.exists(pred_gl_beh_path)) {
  pred_gl <- load_data_with_predictions("Grandes Lignes")
  pred_gl_beh <- predict_behaviour(pred_gl)
  write_fst(pred_gl_beh, pred_gl_beh_path)
} else {
  pred_gl_beh <- read_fst(pred_gl_beh_path)
  setDT(pred_gl_beh)
}
fstcore package v0.9.14
(OpenMP was not detected, using single threaded mode)
if (!file.exists(pred_ss_beh_path)) {
  pred_ss <- load_data_with_predictions("Stepping Stones")
  pred_ss_beh <- predict_behaviour(pred_ss)
  write_fst(pred_ss_beh, pred_ss_beh_path)
} else {
  pred_ss_beh <- read_fst(pred_ss_beh_path)
  setDT(pred_ss_beh)
}
pred_beh <- rbind(pred_gl_beh[, course := "French"],
                  pred_ss_beh[, course := "English"])

rm(pred_gl_beh, pred_ss_beh)
gc()
            used   (Mb) gc trigger   (Mb) limit (Mb)  max used   (Mb)
Ncells   2682960  143.3    4903551  261.9         NA   4128751  220.5
Vcells 242583431 1850.8  655809036 5003.5      32768 565796171 4316.7

Activation

p_act_dist <- ggplot(pred_beh[between(predicted_activation, -2, 0)], aes(x = predicted_activation, fill = prediction_label)) +
  facet_grid(course ~ prediction_label, scales = "free_y") +
  geom_histogram(aes(y = ..density..), binwidth = .01) +
  guides(fill = "none") +
  labs(x = "RT (in s)",
       y = "Density") +
  scale_fill_manual(values = condition_colours)

# p_act_dist

ggsave(plot = p_act_dist, file.path("..", "output", "activation_distribution.png"),
       device = "png", width = 7.5, height = 4.5)

rm(p_act_dist)

Response time

Distribution of observed correct RT (truncated at 25 seconds for readability):

p_rt_dist <- ggplot(pred_beh[correct == 1 & between(rt, 0, 25000)], aes(x = rt/1000)) +
  facet_grid(course ~ ., scales = "free_y") +
  geom_histogram(aes(y = ..density..), binwidth = .1) +
  guides(fill = "none") +
  labs(x = "RT (in s)",
       y = "Density")

# p_rt_dist

ggsave(plot = p_rt_dist, file.path("..", "output", "rt_observed_distribution.png"),
       device = "png", width = 6, height = 6)

rm(p_rt_dist)

Predicted response time

Distribution of predictions

p_rt_pred_dist <- ggplot(pred_beh, aes(x = predicted_rt/1000)) +
  facet_grid(course ~ ., scales = "free_y") +
  geom_histogram(aes(y = ..density..), binwidth = .1) +
  guides(fill = "none") +
  labs(x = "Predicted RT (in s)",
       y = "Density")

# p_rt_pred_dist

ggsave(plot = p_rt_pred_dist, file.path("..", "output", "rt_predicted_distribution.png"),
       device = "png", width = 6, height = 6)

rm(p_rt_pred_dist)

Predicted vs observed values

Calculate absolute prediction error:

pred_rt_error <- (pred_beh
                  [correct == 1]
                  [between(rt, 0, 25000)]
                  [, rt_pred_error := predicted_rt - rt]
                  [, abs_rt_pred_error := abs(rt_pred_error)]
)

pred_rt_error_avg <- pred_rt_error[, .(mae = mean(abs_rt_pred_error),
                                       ae_se = sd(abs_rt_pred_error)/.N), 
                                   by = .(course, prediction_label)]
  
n_obs <- pred_rt_error[, .N, by = .(course, prediction_label)]
# plot_range <- range(pred_beh$predicted_rt/1000, na.rm = TRUE)
plot_range <- c(0, 10)
plot_breaks <- seq(0, 10, by = 2)
# 
# plot_range <- quantile(pred_beh$predicted_rt/1000, c(.005, .995))

p_rt_pred_v_obs <- ggplot(pred_beh[correct == 1], aes(x = predicted_rt/1000, y = rt/1000, colour = prediction_label)) +
  facet_grid(course ~ prediction_label) +
  geom_abline(slope = 1, intercept = 0, lty = 3, alpha = 0.75) +
  geom_point(alpha = .1, size = .1, pch = ".") +
  geom_smooth(method = "lm", formula = y ~ x, colour = "black") +
  geom_label(data = pred_rt_error_avg,
            aes(label = paste("MAE =", formatC(mae/1000, digits = 3, flag = "#"))),
            x = plot_range[2], y = plot_range[1],
            hjust = 1, colour = "NA", size = 3,
            alpha = .9,
            label.size = NA) +
  geom_text(data = pred_rt_error_avg,
            aes(label = paste("MAE =", formatC(mae/1000, digits = 3, flag = "#"))),
            x = plot_range[2], y = plot_range[1],
            hjust = 1, colour = "black", size = 3) +
  geom_label(data = n_obs,
            aes(label = paste("n =", scales::comma(N))),
            x = plot_range[2],
            y = plot_range[2],
            hjust = 1, colour = "NA", size = 3,
            alpha = .9,
            label.size = NA) +
  geom_text(data = n_obs,
            aes(label = paste("n =", scales::comma(N))),
            x = plot_range[2],
            y = plot_range[2],
            hjust = 1, colour = "black", size = 3) +
  guides(colour = "none") +
  labs(x = "Predicted RT (s)",
       y = "Observed RT (s)") +
  coord_fixed(ratio = 1, xlim = plot_range, ylim = plot_range) +
  scale_x_continuous(breaks = plot_breaks) +
  scale_y_continuous(breaks = plot_breaks) +
  scale_colour_manual(values = condition_colours)

p_rt_pred_v_obs

ggsave(plot = p_rt_pred_v_obs, file.path("..", "output", "rt_predicted_vs_observed.png"),
       device = "png", width = 10, height = 4.5)


rm(p_rt_pred_v_obs)

Prediction error

Distribution of prediction error (truncated to [-5, 5] for readability):

p_rt_pred_error <- ggplot(pred_rt_error, aes(x = rt_pred_error/1000, fill = prediction_label)) +
  facet_grid(prediction_label ~ course , scales = "free_y") +
  geom_histogram(aes(y = ..density..), binwidth = .1) +
  guides(fill = "none") +
  labs(x = "RT prediction error in s (predicted - observed)",
       y = "Density") +
  coord_cartesian(xlim = c(-5, 5)) +
  scale_fill_manual(values = condition_colours)

p_rt_pred_error

ggsave(plot = p_rt_pred_error, file.path("..", "output", "rt_prediction_error.png"),
       device = "png", width = 5, height = 7.5)


rm(p_rt_pred_error)

Absolute prediction error

p_rt_abs_pred_error <- ggplot(pred_rt_error, aes(x = abs_rt_pred_error/1000, fill = prediction_label)) +
  facet_grid(prediction_label ~ course, scales = "free_y") +
  geom_histogram(aes(y = ..density..), binwidth = .1) +
  guides(fill = "none") +
  labs(x = "RT prediction error in s (predicted - observed)",
       y = "Density") +
  coord_cartesian(xlim = c(0, 5)) +
  scale_fill_manual(values = condition_colours)

p_rt_abs_pred_error

ggsave(plot = p_rt_abs_pred_error, file.path("..", "output", "rt_absolute_prediction_error.png"),
       device = "png", width = 5, height = 7.5)


rm(p_rt_abs_pred_error)
ggplot(pred_rt_error_avg, aes(x = prediction_label, y = mae/1000, colour = course)) +
  geom_boxplot(data = pred_rt_error,
               aes(y = abs_rt_pred_error/1000, group = interaction(course, prediction_label)),
               colour = "grey70",
               width = .25,
               outlier.shape = NA,
               position = position_dodge(width = .5)) +
  geom_errorbar(aes(ymin = mae/1000 - ae_se/1000, ymax = mae/1000 + ae_se/1000), width = 0, position = position_dodge(width = .5)) +
  geom_point(position = position_dodge(width = .5)) +
  coord_cartesian(ylim = c(0, 2.5)) +
  labs(x = "Method",
       y = "Absolute RT prediction error (in s)",
       colour = "Course")

Fit a regression model on absolute RT prediction error.

French
m_rt_pred_error_gl_file <- file.path("..", "data", "model_fits", "m_rt_pred_error_Grandes_Lignes.rda")

if (file.exists(m_rt_pred_error_gl_file)) {
  load(m_rt_pred_error_gl_file)
} else {
  
  pred_gl_reg <- (
    pred_rt_error
    [course == "French"]
    [sample(.N, 1e6)]
    [, .(prediction_label, abs_rt_pred_error, user_id, fact_id)]
  )
  
  m_rt_pred_error_gl <- lmer(abs_rt_pred_error ~ prediction_label + 
                               (1 | user_id) + (1 | fact_id),
                             data = pred_gl_reg,
                             control = lmerControl(optimizer ="bobyqa"))
  
  save(m_rt_pred_error_gl, file = m_rt_pred_error_gl_file)
}

summary(m_rt_pred_error_gl)
Linear mixed model fit by REML. t-tests use Satterthwaite's method [
lmerModLmerTest]
Formula: 
abs_rt_pred_error ~ prediction_label + (1 | user_id) + (1 | fact_id)
   Data: pred_gl_reg
Control: lmerControl(optimizer = "bobyqa")

REML criterion at convergence: 18047298

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-2.5110 -0.4053 -0.1761  0.0625 11.1385 

Random effects:
 Groups   Name        Variance Std.Dev.
 user_id  (Intercept)  220053   469.1  
 fact_id  (Intercept)  194518   441.0  
 Residual             3843235  1960.4  
Number of obs: 1000000, groups:  user_id, 40820; fact_id, 22762

Fixed effects:
                                 Estimate Std. Error         df t value
(Intercept)                      1433.560      6.509  53722.081 220.257
prediction_labelDomain            -19.468      6.192 974447.696  -3.144
prediction_labelFact             -118.481      6.218 974827.243 -19.056
prediction_labelLearner           -42.334      6.270 975733.404  -6.751
prediction_labelFact & Learner   -101.382      6.300 976077.378 -16.091
                               Pr(>|t|)    
(Intercept)                     < 2e-16 ***
prediction_labelDomain          0.00167 ** 
prediction_labelFact            < 2e-16 ***
prediction_labelLearner        1.46e-11 ***
prediction_labelFact & Learner  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr) prdc_D prdc_F prdc_L
prdctn_lblD -0.474                     
prdctn_lblF -0.468  0.497              
prdctn_lblL -0.462  0.492  0.490       
prdctn_lF&L -0.455  0.490  0.489  0.486

Compare different prediction types to each other:

ht_rt_gl <- glht(m_rt_pred_error_gl, linfct = mcp(prediction_label = "Tukey"))
summary(ht_rt_gl)

     Simultaneous Tests for General Linear Hypotheses

Multiple Comparisons of Means: Tukey Contrasts


Fit: lmer(formula = abs_rt_pred_error ~ prediction_label + (1 | user_id) + 
    (1 | fact_id), data = pred_gl_reg, control = lmerControl(optimizer = "bobyqa"))

Linear Hypotheses:
                              Estimate Std. Error z value Pr(>|z|)    
Domain - Default == 0          -19.468      6.192  -3.144  0.01438 *  
Fact - Default == 0           -118.481      6.218 -19.056  < 0.001 ***
Learner - Default == 0         -42.334      6.270  -6.751  < 0.001 ***
Fact & Learner - Default == 0 -101.382      6.300 -16.091  < 0.001 ***
Fact - Domain == 0             -99.013      6.225 -15.905  < 0.001 ***
Learner - Domain == 0          -22.867      6.278  -3.642  0.00244 ** 
Fact & Learner - Domain == 0   -81.914      6.308 -12.986  < 0.001 ***
Learner - Fact == 0             76.146      6.304  12.079  < 0.001 ***
Fact & Learner - Fact == 0      17.099      6.329   2.702  0.05368 .  
Fact & Learner - Learner == 0  -59.047      6.374  -9.264  < 0.001 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Adjusted p values reported -- single-step method)

Use emmeans to estimate the standardised effect size for each contrast:

emm_rt_gl <- emmeans(m_rt_pred_error_gl, "prediction_label", lmer.df = "asymptotic")
# Sigma calculation follows the example in the eff_size documentation (see also Westfall et al., 2014)
vc_rt_gl <- as.data.frame(VarCorr(m_rt_pred_error_gl)) 
sigma_rt_gl <- sqrt(sum(vc_rt_gl$vcov))
eff_size_rt_gl <- eff_size(emm_rt_gl, sigma = sigma_rt_gl, edf = Inf) # Choice of edf does not affect effect size, only the SE
eff_size_rt_gl
 contrast                 effect.size      SE  df asymp.LCL asymp.UCL
 Default - Domain             0.00943 0.00300 Inf   0.00355   0.01532
 Default - Fact               0.05742 0.00301 Inf   0.05151   0.06332
 Default - Learner            0.02052 0.00304 Inf   0.01456   0.02647
 Default - Fact & Learner     0.04913 0.00305 Inf   0.04315   0.05512
 Domain - Fact                0.04798 0.00302 Inf   0.04207   0.05390
 Domain - Learner             0.01108 0.00304 Inf   0.00512   0.01705
 Domain - Fact & Learner      0.03970 0.00306 Inf   0.03371   0.04569
 Fact - Learner              -0.03690 0.00306 Inf  -0.04289  -0.03091
 Fact - Fact & Learner       -0.00829 0.00307 Inf  -0.01430  -0.00227
 Learner - Fact & Learner     0.02862 0.00309 Inf   0.02256   0.03467

sigma used for effect sizes: 2063 
Degrees-of-freedom method: inherited from asymptotic when re-gridding 
Confidence level used: 0.95 

Inspect the model’s residuals:

qqnorm(resid(m_rt_pred_error_gl))
qqline(resid(m_rt_pred_error_gl), col = "red")

plot(m_rt_pred_error_gl)

English
m_rt_pred_error_ss_file <- file.path("..", "data", "model_fits", "m_rt_pred_error_Stepping_Stones.rda")

if (file.exists(m_rt_pred_error_ss_file)) {
  load(m_rt_pred_error_ss_file)
} else {
  
  pred_ss_reg <- (
    pred_rt_error
    [course == "English"]
    [sample(.N, 1e6)]
    [, .(prediction_label, abs_rt_pred_error, user_id, fact_id)]
  )
  
  m_rt_pred_error_ss <- lmer(abs_rt_pred_error ~ prediction_label + 
                               (1 | user_id) + (1 | fact_id),
                             data = pred_ss_reg,
                             control = lmerControl(optimizer ="bobyqa")
  )
  
  save(m_rt_pred_error_ss, file = m_rt_pred_error_ss_file)
}

summary(m_rt_pred_error_ss)
Linear mixed model fit by REML. t-tests use Satterthwaite's method [
lmerModLmerTest]
Formula: 
abs_rt_pred_error ~ prediction_label + (1 | user_id) + (1 | fact_id)
   Data: pred_ss_reg
Control: lmerControl(optimizer = "bobyqa")

REML criterion at convergence: 18097553

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-1.6170 -0.3973 -0.1997  0.0215 10.9768 

Random effects:
 Groups   Name        Variance Std.Dev.
 user_id  (Intercept)  150645   388.1  
 fact_id  (Intercept)   72213   268.7  
 Residual             4075141  2018.7  
Number of obs: 1000000, groups:  user_id, 85884; fact_id, 45600

Fixed effects:
                                 Estimate Std. Error         df t value
(Intercept)                      1312.875      5.205 161344.300 252.225
prediction_labelDomain            -27.214      6.416 984927.056  -4.242
prediction_labelFact              -58.734      6.448 985192.856  -9.109
prediction_labelLearner           -41.073      6.467 984764.807  -6.351
prediction_labelFact & Learner    -64.932      6.493 985035.484 -10.000
                               Pr(>|t|)    
(Intercept)                     < 2e-16 ***
prediction_labelDomain         2.22e-05 ***
prediction_labelFact            < 2e-16 ***
prediction_labelLearner        2.14e-10 ***
prediction_labelFact & Learner  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr) prdc_D prdc_F prdc_L
prdctn_lblD -0.617                     
prdctn_lblF -0.613  0.498              
prdctn_lblL -0.609  0.496  0.494       
prdctn_lF&L -0.606  0.494  0.492  0.491

Compare different prediction types to each other:

ht_rt_ss <- glht(m_rt_pred_error_ss, linfct = mcp(prediction_label = "Tukey"))
summary(ht_rt_ss)

     Simultaneous Tests for General Linear Hypotheses

Multiple Comparisons of Means: Tukey Contrasts


Fit: lmer(formula = abs_rt_pred_error ~ prediction_label + (1 | user_id) + 
    (1 | fact_id), data = pred_ss_reg, control = lmerControl(optimizer = "bobyqa"))

Linear Hypotheses:
                              Estimate Std. Error z value Pr(>|z|)    
Domain - Default == 0          -27.214      6.416  -4.242  < 0.001 ***
Fact - Default == 0            -58.734      6.448  -9.109  < 0.001 ***
Learner - Default == 0         -41.073      6.467  -6.351  < 0.001 ***
Fact & Learner - Default == 0  -64.932      6.493 -10.000  < 0.001 ***
Fact - Domain == 0             -31.520      6.445  -4.890  < 0.001 ***
Learner - Domain == 0          -13.859      6.466  -2.143  0.20177    
Fact & Learner - Domain == 0   -37.717      6.491  -5.811  < 0.001 ***
Learner - Fact == 0             17.661      6.497   2.718  0.05130 .  
Fact & Learner - Fact == 0      -6.198      6.521  -0.950  0.87701    
Fact & Learner - Learner == 0  -23.859      6.540  -3.648  0.00252 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Adjusted p values reported -- single-step method)

Use emmeans to estimate the standardised effect size for each contrast:

emm_rt_ss <- emmeans(m_rt_pred_error_ss, "prediction_label", lmer.df = "asymptotic")
# Sigma calculation follows the example in the eff_size documentation (see also Westfall et al., 2014)
vc_rt_ss <- as.data.frame(VarCorr(m_rt_pred_error_ss)) 
sigma_rt_ss <- sqrt(sum(vc_rt_ss$vcov))
eff_size_rt_ss <- eff_size(emm_rt_ss, sigma = sigma_rt_ss, edf = Inf) # Choice of edf does not affect effect size, only the SE
eff_size_rt_ss
 contrast                 effect.size      SE  df asymp.LCL asymp.UCL
 Default - Domain             0.01313 0.00309 Inf  0.007061   0.01919
 Default - Fact               0.02833 0.00311 Inf  0.022235   0.03443
 Default - Learner            0.01981 0.00312 Inf  0.013698   0.02593
 Default - Fact & Learner     0.03132 0.00313 Inf  0.025182   0.03746
 Domain - Fact                0.01520 0.00311 Inf  0.009110   0.02130
 Domain - Learner             0.00668 0.00312 Inf  0.000572   0.01280
 Domain - Fact & Learner      0.01819 0.00313 Inf  0.012057   0.02433
 Fact - Learner              -0.00852 0.00313 Inf -0.014661  -0.00238
 Fact - Fact & Learner        0.00299 0.00315 Inf -0.003175   0.00915
 Learner - Fact & Learner     0.01151 0.00315 Inf  0.005325   0.01769

sigma used for effect sizes: 2073 
Degrees-of-freedom method: inherited from asymptotic when re-gridding 
Confidence level used: 0.95 

Inspect the model’s residuals:

qqnorm(resid(m_rt_pred_error_ss))
qqline(resid(m_rt_pred_error_ss), col = "red")

plot(m_rt_pred_error_ss)

Comparison
ht_rt_gl_tidy <- broom::tidy(confint(ht_rt_gl))
ht_rt_ss_tidy <- broom::tidy(confint(ht_rt_ss))
setDT(ht_rt_gl_tidy)
setDT(ht_rt_ss_tidy)

ht_rt_both_tidy <- rbind(ht_rt_gl_tidy[, course := "French"],
                      ht_rt_ss_tidy[, course := "English"])
p_rt_pred_error_comp <- ggplot(ht_rt_both_tidy, aes(x = contrast, y = estimate, ymin = conf.low, ymax = conf.high, colour = course)) +
  geom_hline(yintercept = 0, linetype = "11", colour = "grey60") +
  geom_errorbar(width = 0.1) + 
  geom_point() +
  labs(x = "Linear hypotheses",
       y = "Estimate",
       caption = "Tukey's range test. Error bars show 95% family-wise confidence level.",
       colour = "Course") +
  coord_flip()

p_rt_pred_error_comp

ggsave(plot = p_rt_pred_error_comp, file.path("..", "output", "rt_prediction_error_comparisons.png"),
       device = "png", width = 7.5, height = 5)


rm(p_rt_pred_error_comp)
Summary plot
pred_rt_error_avg[, prediction_rank := frank(-mae), by = .(course)]

annotation_df_ss <- data.table(
  course = rep("English", 10),
  start = c(1, 1, 1, 1,
            2, 2, 2,
            3, 3,
            4
  ),
  end = c(2, 3, 4, 5,
          3, 4, 5,
          4, 5,
          5
  ),
  y = seq(max(pred_rt_error_avg$mae)*1.01 + 45, max(pred_rt_error_avg$mae)*1.01, by = -5),
  label = c("p < .001", "p < .001", "p < .001", "p < .001",
            "n.s.", "p < .001", "p < .001",
            "n.s.", "p < .01",
            "n.s.")
)

annotation_df_gl <- data.table(
  course = rep("French", 10),
  start = c(1, 1, 1, 1,
            2, 2, 2,
            3, 3,
            4
  ),
  end = c(2, 3, 4, 5,
          3, 4, 5,
          4, 5,
          5
  ),
  y = seq(max(pred_rt_error_avg$mae)*1.01 + 45, max(pred_rt_error_avg$mae)*1.01, by = -5),
  label = c("p < .05", "p < .001", "p < .001", "p < .001",
            "p < .01", "p < .001", "p < .001",
            "p < .001", "p < .001",
            "n.s.")
)

annotation_df_rt <- rbind(annotation_df_ss, annotation_df_gl)
annotation_df_rt[, label := factor(label, levels = c("p < .001", "p < .01", "p < .05", "n.s."))]

p_rt_pred_error_summary <- ggplot(pred_rt_error_avg, aes(x = prediction_rank, y = mae)) +
  facet_grid(~ course) +
  geom_line(data = annotation_df_rt,
            aes(x = 1, y = 1300, lty = label, alpha = label, colour = NULL)) + # Dummy line to get legend
  geom_line(aes(colour = course, group = course)) +
  geom_errorbar(aes(ymin = mae - ae_se, ymax = mae + ae_se), width = 0) +
  geom_point(aes(colour = course, group = course)) +
  geom_label(aes(label = prediction_label), 
             colour = "black", 
             alpha = .9,
             label.size = NA, 
             nudge_y = -15) +
  labs(x = NULL,
       y = "Absolute prediction error:\nresponse time (s)",
       colour = "Course") +
  scale_x_continuous(expand = expansion(add = .75), breaks = NULL) +
  scale_y_continuous(labels = scales::comma_format(big.mark = ".")) +
  scale_colour_manual(values = dataset_colours) +
  scale_linetype_manual(values = c("p < .001" = 1,
                                   "p < .01" = 5,
                                   "p < .05" = 2,
                                   "n.s." = 3),
                        name = "Pairwise comparison:") +
  scale_alpha_manual(values = c("p < .001" = 1,
                                "p < .01" = .75,
                                "p < .05" = .5, 
                                "n.s." = .25), 
                     name = "Pairwise comparison:") +
  guides(colour = "none") +
  ggsignif::geom_signif(data = annotation_df_rt,
                        aes(xmin = start, xmax = end, annotations = "", 
                            y_position = y, lty = label, alpha = label),
                        tip_length = 0,
                        manual = TRUE)  +
  theme(legend.position = "bottom",
        legend.justification = "right")
Warning: Ignoring unknown aesthetics: xmin, xmax, annotations, and y_position
p_rt_pred_error_summary

ggsave(file.path("..", "output", "rt_absolute_prediction_error_summary.png"),
       device = "png", width = 10, height = 4)

Improvement

How big was the improvement from worst to best prediction method?

French:

# Absolute change
ht_rt_gl_tidy[contrast == "Fact - Default", estimate[1]]
[1] -118.4807
# % change
scales::percent(
  ht_rt_gl_tidy[contrast == "Fact - Default", estimate[1]] / fixef(m_rt_pred_error_gl)[[1]],
  accuracy = .1)
[1] "-8.3%"
# Associated standardised effect size
eff_size_rt_gl_tidy <- broom::tidy(eff_size_rt_gl) |> as.data.table()
eff_size_rt_gl_tidy[contrast == "Default - Fact", estimate]
[1] 0.05741889

English:

# Absolute change
ht_rt_ss_tidy[contrast == "Fact & Learner - Default", estimate[1]]
[1] -64.93171
# % change
scales::percent(
  ht_rt_ss_tidy[contrast == "Fact & Learner - Default", estimate[1]] / fixef(m_rt_pred_error_ss)[[1]],
  accuracy = .1)
[1] "-4.9%"
# Associated standardised effect size
eff_size_rt_ss_tidy <- broom::tidy(eff_size_rt_ss) |> as.data.table()
eff_size_rt_ss_tidy[contrast == "Default - Fact & Learner", estimate]
[1] 0.03132014

RMSE

The mean absolute prediction error is a linear error metric. We could also choose a non-linear metric that penalises larger deviations more heavily. A common choice would be RMSE (root mean squared error).

pred_rt_error[, .(mae = mean(abs_rt_pred_error),
                  rmse = sqrt(mean(rt_pred_error^2))),
                  by = .(course, prediction_label)] |>
  ggplot(aes(x = prediction_label, y = rmse, group = course, colour = course)) +
  geom_line() +
  geom_point()

Response accuracy

Predicted response accuracy

Distribution of predictions

plot_dodge <- function(y, dodge = .1) {
  return (y * (1 + dodge) - dodge/2)
}

Predicted vs observed values

plot_dodge <- function(y, dodge = .1) {
  return (y * (1 + dodge) - dodge/2)
}
p_acc_pred_v_obs <- ggplot(pred_beh, aes(x = predicted_accuracy, y = correct, group = prediction_label, colour = prediction_label, fill = prediction_label)) +
    facet_grid(course ~ prediction_label) +
    geom_point(aes(y = correct),
               position = position_jitter(width = 0, height = .025, seed = 123),
               size = .001, pch = ".", alpha = .1) +
    labs(x = "Predicted accuracy",
         y = "Response accuracy",
         colour = "Prediction method",
         fill = "Prediction method") +
  guides(colour = "none",
         fill = "none") +
    scale_x_continuous(breaks = seq(0, 1, by = .25), labels = scales::percent_format()) +
    scale_y_continuous(breaks = seq(0, 1, by = .25), labels = scales::percent_format()) +
    scale_colour_manual(values = condition_colours) +
    scale_fill_manual(values = condition_colours) +
    coord_cartesian(xlim = c(0, 1), ylim = c(0, 1), clip = "off")

p_acc_pred_v_obs

ggsave(plot = p_acc_pred_v_obs, file.path("..", "output", "acc_predicted_vs_observed.png"),
       device = "png", width = 10, height = 4.5)


rm(p_acc_pred_v_obs)

Prediction error

pred_acc_error <- (pred_beh
                   [, acc_pred_error := predicted_accuracy - correct]
                   [, abs_acc_pred_error := abs(acc_pred_error)])


pred_acc_error_avg <- pred_acc_error[, .(mae = mean(abs_acc_pred_error),
                                         ae_se = sd(abs_acc_pred_error)/.N), 
                                     by = .(course, prediction_label)]

n_obs <- pred_acc_error[, .N, by = .(course, prediction_label)]

Distribution of prediction error:

p_acc_pred_error <- ggplot(pred_acc_error, aes(x = acc_pred_error, fill = prediction_label)) +
  facet_grid(prediction_label ~ course , scales = "free_y") +
  geom_histogram(aes(y = ..density..), binwidth = .01) +
  guides(fill = "none") +
  labs(x = "Accuracy prediction error (predicted - observed)",
       y = "Density") +
  coord_cartesian(xlim = c(-1, 1)) +
  scale_fill_manual(values = condition_colours)

p_acc_pred_error

ggsave(plot = p_acc_pred_error, file.path("..", "output", "acc_prediction_error.png"),
       device = "png", width = 5, height = 7.5)

rm(p_acc_pred_error)

Absolute prediction error

ggplot(pred_acc_error_avg, aes(x = prediction_label, y = mae, colour = course)) +
  geom_boxplot(data = pred_acc_error,
               aes(y = abs_acc_pred_error, group = interaction(course, prediction_label)),
               colour = "grey70",
               width = .25,
               outlier.shape = NA,
               position = position_dodge(width = .5)) +
  geom_errorbar(aes(ymin = mae - ae_se, ymax = mae + ae_se), width = 0, position = position_dodge(width = .5)) +
  geom_point(position = position_dodge(width = .5)) +
  coord_cartesian(ylim = c(0, 1)) +
  labs(x = "Method",
       y = "Absolute accuracy prediction error",
       colour = "Course")

ggplot(pred_acc_error_avg, aes(x = prediction_label, y = mae, colour = course)) +
  geom_boxplot(data = pred_acc_error,
               aes(y = abs_acc_pred_error, group = interaction(course, prediction_label)),
               colour = "grey70",
               width = .25,
               outlier.shape = NA,
               position = position_dodge(width = .5)) +
  geom_errorbar(aes(ymin = mae - ae_se, ymax = mae + ae_se), width = 0, position = position_dodge(width = .5)) +
  geom_point(position = position_dodge(width = .5)) +
  coord_cartesian(ylim = c(0, 1)) +
  labs(x = "Method",
       y = "Absolute accuracy prediction error",
       colour = "Course")

Fit a regression model.

French
m_acc_pred_error_gl_file <- file.path("..", "data", "model_fits", "m_acc_pred_error_Grandes_Lignes_new.rda")

if (file.exists(m_acc_pred_error_gl_file)) {
  load(m_acc_pred_error_gl_file)
} else {
  
  pred_gl_reg <- (
    pred_acc_error
    [course == "French"]
    [sample(.N, 1e6)]
    [, .(prediction_label, abs_acc_pred_error, user_id, fact_id)]
  )
  
  m_acc_pred_error_gl <- lmer(abs_acc_pred_error ~ prediction_label + 
                               (1 | user_id) + (1 | fact_id),
                             data = pred_gl_reg,
                             control = lmerControl(optimizer ="bobyqa"))
    
  save(m_acc_pred_error_gl, file = m_acc_pred_error_gl_file)
}

summary(m_acc_pred_error_gl)
Linear mixed model fit by REML. t-tests use Satterthwaite's method [
lmerModLmerTest]
Formula: abs_acc_pred_error ~ prediction_label + (1 | user_id) + (1 |  
    fact_id)
   Data: pred_gl_reg
Control: lmerControl(optimizer = "bobyqa")

REML criterion at convergence: -1051298

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-4.5692 -0.5355 -0.0828  0.4664  4.9073 

Random effects:
 Groups   Name        Variance Std.Dev.
 user_id  (Intercept) 0.001687 0.04108 
 fact_id  (Intercept) 0.002174 0.04662 
 Residual             0.019154 0.13840 
Number of obs: 1000000, groups:  user_id, 41014; fact_id, 22541

Fixed effects:
                                 Estimate Std. Error         df t value
(Intercept)                     4.780e-01  5.493e-04  5.561e+04  870.21
prediction_labelDomain         -2.752e-02  4.385e-04  9.736e+05  -62.76
prediction_labelFact           -3.682e-02  4.409e-04  9.734e+05  -83.50
prediction_labelLearner        -2.963e-02  4.447e-04  9.745e+05  -66.63
prediction_labelFact & Learner -4.948e-02  4.466e-04  9.743e+05 -110.80
                               Pr(>|t|)    
(Intercept)                      <2e-16 ***
prediction_labelDomain           <2e-16 ***
prediction_labelFact             <2e-16 ***
prediction_labelLearner          <2e-16 ***
prediction_labelFact & Learner   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr) prdc_D prdc_F prdc_L
prdctn_lblD -0.400                     
prdctn_lblF -0.391  0.498              
prdctn_lblL -0.387  0.494  0.491       
prdctn_lF&L -0.379  0.492  0.490  0.487

Compare different prediction types to each other:

qqnorm(resid(m_acc_pred_error_gl))
qqline(resid(m_acc_pred_error_gl), col = "red")

Use emmeans to estimate the standardised effect size for each contrast:

plot(m_acc_pred_error_gl)

Inspect the model’s residuals:

qqnorm(resid(m_acc_pred_error_gl))
qqline(resid(m_acc_pred_error_gl), col = "red")
plot(m_acc_pred_error_gl)
English
m_acc_pred_error_ss_file <- file.path("..", "data", "model_fits", "m_acc_pred_error_Stepping_Stones_new.rda")

if (file.exists(m_acc_pred_error_ss_file)) {
  load(m_acc_pred_error_ss_file)
} else {
  
  pred_ss_reg <- (
    pred_acc_error
    [course == "English"]
    [sample(.N, 1e6)]
    [, .(prediction_label, abs_acc_pred_error, user_id, fact_id)]
  )
  
  m_acc_pred_error_ss <- lmer(abs_acc_pred_error ~ prediction_label + 
                               (1 | user_id) + (1 | fact_id),
                             data = pred_ss_reg,
                             control = lmerControl(optimizer ="bobyqa")
  )
  
  save(m_acc_pred_error_ss, file = m_acc_pred_error_ss_file)
}

summary(m_acc_pred_error_ss)
Linear mixed model fit by REML. t-tests use Satterthwaite's method [
lmerModLmerTest]
Formula: abs_acc_pred_error ~ prediction_label + (1 | user_id) + (1 |  
    fact_id)
   Data: pred_ss_reg
Control: lmerControl(optimizer = "bobyqa")

REML criterion at convergence: -1182614

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-4.5639 -0.4544 -0.0844  0.3632  6.8129 

Random effects:
 Groups   Name        Variance Std.Dev.
 user_id  (Intercept) 0.001618 0.04022 
 fact_id  (Intercept) 0.001753 0.04187 
 Residual             0.016321 0.12775 
Number of obs: 1000000, groups:  user_id, 86095; fact_id, 45414

Fixed effects:
                                 Estimate Std. Error         df t value
(Intercept)                     4.472e-01  4.203e-04  1.179e+05 1064.06
prediction_labelDomain         -3.082e-02  4.115e-04  9.634e+05  -74.89
prediction_labelFact           -3.611e-02  4.136e-04  9.631e+05  -87.31
prediction_labelLearner        -3.341e-02  4.150e-04  9.628e+05  -80.50
prediction_labelFact & Learner -4.628e-02  4.163e-04  9.617e+05 -111.16
                               Pr(>|t|)    
(Intercept)                      <2e-16 ***
prediction_labelDomain           <2e-16 ***
prediction_labelFact             <2e-16 ***
prediction_labelLearner          <2e-16 ***
prediction_labelFact & Learner   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr) prdc_D prdc_F prdc_L
prdctn_lblD -0.489                     
prdctn_lblF -0.483  0.497              
prdctn_lblL -0.481  0.496  0.493       
prdctn_lF&L -0.476  0.494  0.492  0.491

Compare different prediction types to each other:

qqnorm(resid(m_acc_pred_error_ss))
qqline(resid(m_acc_pred_error_ss), col = "red")

Use emmeans to estimate the standardised effect size for each contrast:

plot(m_acc_pred_error_ss)

Inspect the model’s residuals:

ht_acc_gl_tidy <- broom::tidy(confint(ht_acc_gl))
ht_acc_ss_tidy <- broom::tidy(confint(ht_acc_ss))
setDT(ht_acc_gl_tidy)
setDT(ht_acc_ss_tidy)

ht_acc_both_tidy <- rbind(ht_acc_gl_tidy[, course := "French"],
                      ht_acc_ss_tidy[, course := "English"])
plot(m_acc_pred_error_ss)
Comparison
ht_acc_gl_tidy <- broom::tidy(confint(ht_acc_gl))
ht_acc_ss_tidy <- broom::tidy(confint(ht_acc_ss))
setDT(ht_acc_gl_tidy)
setDT(ht_acc_ss_tidy)

ht_acc_both_tidy <- rbind(ht_acc_gl_tidy[, course := "French"],
                      ht_acc_ss_tidy[, course := "English"])
p_acc_pred_error_comp <- ggplot(ht_acc_both_tidy, aes(x = contrast, y = estimate, ymin = conf.low, ymax = conf.high, colour = course)) +
  geom_hline(yintercept = 0, linetype = "11", colour = "grey60") +
  geom_errorbar(width = 0.1) + 
  geom_point() +
  labs(x = "Linear hypotheses",
       y = "Estimate",
       caption = "Tukey's range test. Error bars show 95% family-wise confidence level.",
       colour = "Course") +
  coord_flip()

p_acc_pred_error_comp

ggsave(plot = p_acc_pred_error_comp, file.path("..", "output", "acc_prediction_error_comparisons.png"),
       device = "png", width = 7.5, height = 5)


rm(p_acc_pred_error_comp)
Summary plot
pred_acc_error_avg[, prediction_rank := frank(-mae), by = .(course)]

annotation_df_ss <- data.table(
  course = rep("English", 10),
  start = c(1, 1, 1, 1,
            2, 2, 2,
            3, 3,
            4
  ),
  end = c(2, 3, 4, 5,
          3, 4, 5,
          4, 5,
          5
  ),
  y = seq(max(pred_acc_error_avg$mae)*1.01 + .0225, max(pred_acc_error_avg$mae)*1.01, by = -.0025),
  label = c("p < .001", "p < .001", "p < .001", "p < .001",
            "p < .001", "p < .001", "p < .001",
            "p < .001", "p < .001",
            "p < .001")
)

annotation_df_gl <- data.table(
  course = rep("French", 10),
  start = c(1, 1, 1, 1,
            2, 2, 2,
            3, 3,
            4
  ),
  end = c(2, 3, 4, 5,
          3, 4, 5,
          4, 5,
          5
  ),
  y = seq(max(pred_acc_error_avg$mae)*1.01 + .0225, max(pred_acc_error_avg$mae)*1.01, by = -.0025),
  label = c("p < .001", "p < .001", "p < .001", "p < .001",
            "p < .001", "p < .001", "p < .001",
            "p < .001", "p < .001",
            "p < .001")
)

annotation_df_acc <- rbind(annotation_df_ss, annotation_df_gl)
annotation_df_acc[, label := factor(label, levels = c("p < .001", "p < .01", "p < .05", "n.s."))]

p_acc_pred_error_summary <- ggplot(pred_acc_error_avg, aes(x = prediction_rank, y = mae)) +
  facet_grid(~ course) +
  geom_line(data = annotation_df_acc,
            aes(x = 1, y = .45, lty = label, alpha = label, colour = NULL)) + # Dummy line to get legend
  geom_line(aes(colour = course, group = course)) +
  geom_errorbar(aes(ymin = mae - ae_se, ymax = mae + ae_se), width = 0) +
  geom_point(aes(colour = course, group = course)) +
  geom_label(aes(label = prediction_label), 
             colour = "black", 
             alpha = .9,
             label.size = NA, 
             nudge_y = -.007) +
  labs(x = NULL,
       y = "Absolute prediction error:\nresponse accuracy",
       colour = "Course") +
  scale_x_continuous(expand = expansion(add = .75), breaks = NULL) +
  scale_colour_manual(values = dataset_colours) +
  scale_linetype_manual(values = c("p < .001" = 1,
                                   "p < .01" = 5,
                                   "p < .05" = 2,
                                   "n.s." = 3),
                        drop = FALSE,
                        name = "Pairwise comparison:") +
  scale_alpha_manual(values = c("p < .001" = 1,
                                "p < .01" = .75,
                                "p < .05" = .5, 
                                "n.s." = .25), 
                     drop = FALSE,
                     name = "Pairwise comparison:") +
  guides(colour = "none") +
  ggsignif::geom_signif(data = annotation_df_acc,
                        aes(xmin = start, xmax = end, annotations = "", 
                            y_position = y, lty = label, alpha = label),
                        tip_length = 0,
                        manual = TRUE)  +
  theme(legend.position = "bottom",
        legend.justification = "right")
Warning: Ignoring unknown aesthetics: xmin, xmax, annotations, and y_position
p_acc_pred_error_summary

ggsave(file.path("..", "output", "acc_absolute_prediction_error_summary.png"),
       device = "png", width = 10, height = 4)

Improvement

How big was the improvement from worst to best prediction method?

French:

# Absolute change
ht_acc_ss_tidy[contrast == "Fact & Learner - Default", estimate[1]]
[1] -0.0462795
# % change
scales::percent(
  ht_acc_ss_tidy[contrast == "Fact & Learner - Default", estimate[1]] / fixef(m_acc_pred_error_ss)[[1]],
  accuracy = .1)
[1] "-10.3%"
# Associated standardised effect size
eff_size_acc_ss_tidy <- broom::tidy(eff_size_acc_ss) |> as.data.table()
eff_size_acc_ss_tidy[contrast == "Default - Fact & Learner", estimate]
[1] 0.3297989

English:

(p_acc_pred_error_summary + p_rt_pred_error_summary) + 
  plot_layout(ncol = 1, guides = "collect") +
  plot_annotation(tag_levels = "A") &
  theme(legend.position = "bottom")

ggsave(file.path("..", "output", "beh_absolute_prediction_error_summary.png"),
       device = "png", width = 10, height = 8)

Combined plot

(p_acc_pred_error_summary + p_rt_pred_error_summary) + 
  plot_layout(ncol = 1, guides = "collect") +
  plot_annotation(tag_levels = "A") &
  theme(legend.position = "bottom")

ggsave(file.path("..", "output", "beh_absolute_prediction_error_summary.png"),
       device = "png", width = 10, height = 8)

Session info

sessionInfo()
LS0tCnRpdGxlOiAiRXZhbHVhdGUgYmVoYXZpb3VyYWwgcHJlZGljdGlvbnMiCmF1dGhvcjogIk1hYXJ0ZW4gdmFuIGRlciBWZWxkZSIKZGF0ZTogIkxhc3QgdXBkYXRlZDogYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgc21hcnQ6IG5vCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICBnaXRodWJfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCgojIE92ZXJ2aWV3CgpUaGlzIG5vdGVib29rIGV2YWx1YXRlcyB0aGUgYmVoYXZpb3VyYWwgcHJlZGljdGlvbnMgKFJULCBhY2N1cmFjeSkgdGhhdCBmb2xsb3cgZnJvbSB0aGUgcHJlZGljdGVkIHJhdGVzIG9mIGZvcmdldHRpbmcuCldlIHNpbXVsYXRlIHRoZSBiZWhhdmlvdXJhbCBwcmVkaWN0aW9ucyB0aGF0IHRoZSBhZGFwdGl2ZSBmYWN0IGxlYXJuaW5nIHN5c3RlbSB3b3VsZCBoYXZlIG1hZGUsIGlmIGl0IGhhZCB1c2VkIHRoZSBwcmVkaWN0ZWQgcmF0ZSBvZiBmb3JnZXR0aW5nIGFzIHRoZSBzdGFydGluZyBlc3RpbWF0ZSBpbiB0aGUgbGVhcm5pbmcgc2VxdWVuY2UuCgoKIyBTZXR1cAoKYGBge3J9CmxpYnJhcnkoZnN0KQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkocHVycnIpCmxpYnJhcnkoZnVycnIpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeSh3ZXNhbmRlcnNvbikKbGlicmFyeShsbWU0KQpsaWJyYXJ5KGxtZXJUZXN0KQpsaWJyYXJ5KG11bHRjb21wKQpsaWJyYXJ5KGVtbWVhbnMpCmBgYAoKYGBge3J9CnNvdXJjZShmaWxlLnBhdGgoIi4uIiwgInNjcmlwdHMiLCAiOTlfc2xpbXN0YW1wZW5fbW9kZWxfZnVucy5SIikpCmBgYAoKYGBge3J9CmZ1dHVyZTo6cGxhbigibXVsdGlzZXNzaW9uIiwgd29ya2VycyA9IDkpICMgU2V0IHRvIGRlc2lyZWQgbnVtYmVyIG9mIGNvcmVzCmBgYAoKYGBge3J9CnRoZW1lX3NldCh0aGVtZV9saWdodChiYXNlX3NpemUgPSAxNCkgKwogICAgICAgICAgICB0aGVtZShzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIpKSkKCmNvbmRpdGlvbl9jb2xvdXJzIDwtIHdlc19wYWxldHRlKCJEYXJqZWVsaW5nMSIsIG4gPSA1KQpjb25kaXRpb25fY29sb3Vyc1tjKDIsIDQsIDUpXSA8LSBjb25kaXRpb25fY29sb3Vyc1tjKDQsIDUsIDIpXQoKZGF0YXNldF9jb2xvdXJzIDwtIHdlc19wYWxldHRlKCJEYXJqZWVsaW5nMiIsIG4gPSA1KVtjKDIsIDMpXQpgYGAKCgojIyBIZWxwZXIgZnVuY3Rpb25zCgpgYGB7cn0KbG9hZF9kYXRhX3dpdGhfcHJlZGljdGlvbnMgPC0gZnVuY3Rpb24gKGNvdXJzZSkgewogIAogICMgRGF0YQogIGRfZnVsbCA8LSByZWFkX2ZzdChmaWxlLnBhdGgoIi4uIiwgImRhdGEiLCBwYXN0ZTAoImZvcm1hdHRlZF8iLCBzdHJfcmVwbGFjZV9hbGwoY291cnNlLCAiICIsICJfIiksICIuZnN0IikpKQogIHNldERUKGRfZnVsbCkKICBkIDwtIGRfZnVsbFshaXMubmEoZmFjdF9pZCksIC4odXNlcl9pZCwgZmFjdF9pZCwgc3RhcnRfdGltZSwgcnQsIGNvcnJlY3QpXQogIHJtKGRfZnVsbCkKICBnYygpCiAgc2V0b3JkZXIoZCwgdXNlcl9pZCwgZmFjdF9pZCwgc3RhcnRfdGltZSkKICAKICAjIFJPRiBwcmVkaWN0aW9ucwogIHByZWRfdXNlciA8LSByZWFkX2ZzdChmaWxlLnBhdGgoIi4uIiwgImRhdGEiLCAicHJlZGljdGlvbnMiLCBwYXN0ZTAoInByZWRfdl9vYnNfdXNlcl8iLCBzdHJfcmVwbGFjZV9hbGwoY291cnNlLCAiICIsICJfIiksICIuZnN0IikpKQogIHByZWRfZmFjdCA8LSByZWFkX2ZzdChmaWxlLnBhdGgoIi4uIiwgImRhdGEiLCAicHJlZGljdGlvbnMiLCBwYXN0ZTAoInByZWRfdl9vYnNfZmFjdF8iLCBzdHJfcmVwbGFjZV9hbGwoY291cnNlLCAiICIsICJfIiksICIuZnN0IikpKQogIHByZWRfZmFjdF91c2VyIDwtIHJlYWRfZnN0KGZpbGUucGF0aCgiLi4iLCAiZGF0YSIsICJwcmVkaWN0aW9ucyIsIHBhc3RlMCgicHJlZF9mYWN0X2FuZF91c2VyXyIsIHN0cl9yZXBsYWNlX2FsbChjb3Vyc2UsICIgIiwgIl8iKSwgIi5mc3QiKSkpCiAgc2V0RFQocHJlZF91c2VyKQogIHNldERUKHByZWRfZmFjdCkKICBzZXREVChwcmVkX2ZhY3RfdXNlcikKICAKICAjIFJlbW92ZSBOQSBhbmQgZHVwbGljYXRlcwogIHByZWRfdXNlciA8LSB1bmlxdWUocHJlZF91c2VyWyFpcy5uYShhbHBoYSldKQogIHByZWRfZmFjdCA8LSB1bmlxdWUocHJlZF9mYWN0WyFpcy5uYShhbHBoYSldKQogIHByZWRfZmFjdF91c2VyIDwtIHVuaXF1ZShwcmVkX2ZhY3RfdXNlclshaXMubmEoYWxwaGEpXSkKICAKICAjIE1ha2UgRG9tYWluIHByZWRpY3Rpb24KICBwcmVkX2RvbWFpbiA8LSBtZWFuKHVuaXF1ZShwcmVkX2ZhY3QsIGJ5ID0gYygiZmFjdF9pZCIpKSRwcmVkX2ZhY3QpCiAgcHJlZF9kZWZhdWx0IDwtIDAuMwogIAogICMgQ29tYmluZQogIHNldG5hbWVzKHByZWRfdXNlciwgIm5fdHJhaW5fb2JzIiwgIm5fdHJhaW5fb2JzX3VzZXIiKQogIHNldG5hbWVzKHByZWRfZmFjdCwgIm5fdHJhaW5fb2JzIiwgIm5fdHJhaW5fb2JzX2ZhY3QiKQogIHByZWRfYWxsIDwtIG1lcmdlKHByZWRfdXNlciwgcHJlZF9mYWN0LCBieSA9IGMoInVzZXJfaWQiLCAiZmFjdF9pZCIsICJhbHBoYSIsICJuX3JlcHMiKSwgYWxsID0gVFJVRSkKICBwcmVkX2FsbCA8LSBtZXJnZShwcmVkX2FsbCwgcHJlZF9mYWN0X3VzZXIsIGJ5ID0gYygidXNlcl9pZCIsICJmYWN0X2lkIiwgImFscGhhIiksIGFsbCA9IFRSVUUpCiAgcHJlZF9hbGxbLCBwcmVkX2RlZmF1bHQgOj0gcHJlZF9kZWZhdWx0XQogIHByZWRfYWxsWywgcHJlZF9kb21haW4gOj0gcHJlZF9kb21haW5dCgogIGRfcHJlcCA8LSBkWywgLih1c2VyX2lkLAogICAgICAgICAgICAgICAgICBmYWN0X2lkLAogICAgICAgICAgICAgICAgICB0ZXh0ID0gIiIsCiAgICAgICAgICAgICAgICAgIHN0YXJ0X3RpbWUsCiAgICAgICAgICAgICAgICAgIHJ0LAogICAgICAgICAgICAgICAgICBjb3JyZWN0LAogICAgICAgICAgICAgICAgICB0aHJlc2hvbGQgPSAtMC44KV0KICAKICBkX3ByZWQgPC0gbWVyZ2UocHJlZF9hbGwsIGRfcHJlcCwgYnkgPSBjKCJ1c2VyX2lkIiwgImZhY3RfaWQiKSkKICAKICByZXR1cm4oZF9wcmVkKQp9CgpwcmVkaWN0X2JlaGF2aW91ciA8LSBmdW5jdGlvbiAoZCkgewogIAogICAgIyBQcm9jZXNzIHRoZSBkYXRhIGluIG1hbmFnZWFibGUgY2h1bmtzCiAgICBjaHVua19zaXplIDwtIDFlNAogICAgb2JzIDwtIHVuaXF1ZShkWywgLih1c2VyX2lkLCBmYWN0X2lkKV0pCiAgICBvYnNfY2h1bmtzIDwtIGMoc2VxKDEsIG5yb3cob2JzKSwgYnkgPSBjaHVua19zaXplKSwgbnJvdyhvYnMpKzEpCiAgICAKICAgIHByZWRfYmVoIDwtIG1hcF9kZnIoMToobGVuZ3RoKG9ic19jaHVua3MpLTEpLCBmdW5jdGlvbiAoaSkgewogICAgICAKICAgICAgbXNnIDwtIHBhc3RlKCJDaHVuayIsIGksICIvIiwgbGVuZ3RoKG9ic19jaHVua3MpLTEpCiAgICAgIHN5c3RlbShwYXN0ZSgiZWNobyIsIG1zZykpCiAgICAgIAogICAgICBvYnNfaSA8LSBvYnNbb2JzX2NodW5rc1tpXTpvYnNfY2h1bmtzW2krMV0tMV0KICAgICAgZF9pIDwtIGRbb2JzX2ksIG9uID0gLih1c2VyX2lkLCBmYWN0X2lkKV0KICAgICAgZF9pX2xpc3QgPC0gc3BsaXQoZF9pLCBieSA9IGMoInVzZXJfaWQiLCAiZmFjdF9pZCIpLCBkcm9wID0gVFJVRSkKICAgICAgCiAgICAgIHByZWRfYmVoX2kgPC0gZnV0dXJlX21hcF9kZnIoZF9pX2xpc3QsIGZ1bmN0aW9uIChsZWFybl9zZXEpIHsKICAgICAgICAKICAgICAgICAjIE9yZ2FuaXNlIHByZWRpY3Rpb25zIGZvciB0aGlzIHNlcXVlbmNlCiAgICAgICAgbGVhcm5fc2VxX3ByZWRzIDwtIHBpdm90X2xvbmdlcigKICAgICAgICAgIGxlYXJuX3NlcVsxLF0sIAogICAgICAgICAgcHJlZF91c2VyOnByZWRfZG9tYWluLAogICAgICAgICAgbmFtZXNfdG8gPSAicHJlZGljdGlvbl90eXBlIiwKICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJwcmVkXyIsCiAgICAgICAgICB2YWx1ZXNfdG8gPSAicHJlZGljdGVkX2FscGhhIgogICAgICAgICkKICAgICAgICBzZXREVChsZWFybl9zZXFfcHJlZHMpCiAgICAgICAgCiAgICAgICAgIyBMb29rIG9ubHkgYXQgdHJpYWwgMyBpbiBlYWNoIHNlcXVlbmNlCiAgICAgICAgdHJpYWwgPC0gbGVhcm5fc2VxWzMsXQogICAgICAgIGRlbGF5IDwtIGxlYXJuX3NlcVsyOjMsIGRpZmYoc3RhcnRfdGltZSldCiAgICAgICAgCiAgICAgICAgIyBDYWxjdWxhdGUgYmVoYXZpb3VyYWwgcHJlZGljdGlvbnMgZm9yIGVhY2ggcHJlZGljdGlvbiBtZXRob2QKICAgICAgICBtYXBfZGZyKHNlcShucm93KGxlYXJuX3NlcV9wcmVkcykpLCBmdW5jdGlvbiAoaikgewogICAgICAgICAgCiAgICAgICAgICBwcmVkaWN0aW9uX3R5cGUgPC0gbGVhcm5fc2VxX3ByZWRzW2osIHByZWRpY3Rpb25fdHlwZV0KICAgICAgICAgIHByZWRpY3RlZF9hbHBoYSA8LSBsZWFybl9zZXFfcHJlZHNbaiwgcHJlZGljdGVkX2FscGhhXQogICAgCiAgICAgICAgICBwcmVkaWN0ZWRfYWN0aXZhdGlvbiA8LSBOQQogICAgICAgICAgcHJlZGljdGVkX2FjY3VyYWN5IDwtIE5BCiAgICAgICAgICBwcmVkaWN0ZWRfcnQgPC0gTkEKICAgIAogICAgICAgICAgaWYgKCFpcy5uYShwcmVkaWN0ZWRfYWxwaGEpKSB7CiAgICAgICAgICAgIAogICAgICAgICAgICBwcmVkaWN0ZWRfYWN0aXZhdGlvbiA8LSBjYWxjdWxhdGVfYWN0aXZhdGlvbigKICAgICAgICAgICAgICB0aW1lID0gdHJpYWxbLCBzdGFydF90aW1lXSwKICAgICAgICAgICAgICBpZCA9IHRyaWFsWywgZmFjdF9pZF0sCiAgICAgICAgICAgICAgZmFjdGFscGhhID0gcHJlZGljdGVkX2FscGhhLAogICAgICAgICAgICAgIHJlc3BvbnNlcyA9IGxlYXJuX3NlcVsxOjIsXQogICAgICAgICAgICApCiAgICAgIAogICAgICAgICAgICBwcmVkaWN0ZWRfYWNjdXJhY3kgPC0gcF9yZWNhbGwoCiAgICAgICAgICAgICAgYWN0aXZhdGlvbiA9IHByZWRpY3RlZF9hY3RpdmF0aW9uLAogICAgICAgICAgICAgIHRocmVzaG9sZCA9IC0wLjgsCiAgICAgICAgICAgICAgIyBhY3RpdmF0aW9uX25vaXNlID0gMC41CiAgICAgICAgICAgICAgYWN0aXZhdGlvbl9ub2lzZSA9IDAuMgogICAgICAgICAgICApCiAgICAgICAgICAgIAogICAgICAgICAgICBwcmVkaWN0ZWRfcnQgPC0gZXN0aW1hdGVfcmVhY3Rpb25fdGltZV9mcm9tX2FjdGl2YXRpb24oCiAgICAgICAgICAgICAgYWN0aXZhdGlvbiA9IHByZWRpY3RlZF9hY3RpdmF0aW9uLAogICAgICAgICAgICAgIHJlYWRpbmdfdGltZSA9IDMwMAogICAgICAgICAgICApCiAgICAgICAgICB9CiAgICAgICAgICAKICAgICAgICAgIHJldHVybigKICAgICAgICAgICAgbGlzdCgKICAgICAgICAgICAgICB1c2VyX2lkID0gdHJpYWxbLCB1c2VyX2lkXSwKICAgICAgICAgICAgICBmYWN0X2lkID0gdHJpYWxbLCBmYWN0X2lkXSwKICAgICAgICAgICAgICBkZWxheSA9IGRlbGF5LAogICAgICAgICAgICAgIGNvcnJlY3QgPSB0cmlhbFssIGNvcnJlY3RdLAogICAgICAgICAgICAgIHJ0ID0gdHJpYWxbLCBydF0sCiAgICAgICAgICAgICAgcHJlZGljdGlvbl90eXBlID0gcHJlZGljdGlvbl90eXBlLAogICAgICAgICAgICAgIHByZWRpY3RlZF9hbHBoYSA9IHByZWRpY3RlZF9hbHBoYSwKICAgICAgICAgICAgICBwcmVkaWN0ZWRfYWN0aXZhdGlvbiA9IHByZWRpY3RlZF9hY3RpdmF0aW9uLAogICAgICAgICAgICAgIHByZWRpY3RlZF9hY2N1cmFjeSA9IHByZWRpY3RlZF9hY2N1cmFjeSwKICAgICAgICAgICAgICBwcmVkaWN0ZWRfcnQgPSBwcmVkaWN0ZWRfcnQKICAgICAgICAgICAgKQogICAgICAgICAgKQogICAgICAgICAgCiAgICAgICAgfSkKICAgICAgfSkKICAgIH0pCiAgICAKICAgIHNldERUKHByZWRfYmVoKQogICAgCiAgICAjIFJlbW92ZSByb3dzIHdpdGhvdXQgcHJlZGljdGlvbgogICAgcHJlZF9iZWggPC0gcHJlZF9iZWhbIWlzLm5hKHByZWRpY3RlZF9hbHBoYSldCiAgICBwcmVkX2JlaCA8LSBwcmVkX2JlaFshaXMuaW5maW5pdGUocHJlZGljdGVkX3J0KV0KICAgIAogICAgIyBTZXQgcHJvcGVyIGNvbmRpdGlvbiBsYWJlbHMKICAgIGNvbmRpdGlvbl9sYWJlbHMgPC0gZGF0YS50YWJsZSgKICAgICAgcHJlZGljdGlvbl90eXBlID0gYygiZGVmYXVsdCIsICJkb21haW4iLCAiZmFjdCIsICJ1c2VyIiwgImZhY3RfdXNlciIpLAogICAgICBwcmVkaWN0aW9uX2xhYmVsID0gZmFjdG9yKAogICAgICAgIGMoIkRlZmF1bHQiLCAiRG9tYWluIiwgIkZhY3QiLCAiTGVhcm5lciIsICJGYWN0ICYgTGVhcm5lciIpLAogICAgICAgIGxldmVscyA9IGMoIkRlZmF1bHQiLCAiRG9tYWluIiwgIkZhY3QiLCAiTGVhcm5lciIsICJGYWN0ICYgTGVhcm5lciIpCiAgICAgICkKICAgICkKICAgIHByZWRfYmVoIDwtIHByZWRfYmVoW2NvbmRpdGlvbl9sYWJlbHMsIG9uID0gLihwcmVkaWN0aW9uX3R5cGUpXQogICAgCiAgICAKICAgIHJldHVybihwcmVkX2JlaCkKfQpgYGAKCgojIENhbGN1bGF0ZSBwcmVkaWN0aW9ucwoKQ2FsY3VsYXRlIGJlaGF2aW91cmFsIHByZWRpY3Rpb25zIGZvciB0cmlhbCAzOgpgYGB7cn0KcHJlZF9nbF9iZWhfcGF0aCA8LSBmaWxlLnBhdGgoIi4uIiwgImRhdGEiLCAicHJlZGljdGlvbnMiLCAicHJlZF9iZWhhdmlvdXJfZ2wuZnN0IikKcHJlZF9zc19iZWhfcGF0aCA8LSBmaWxlLnBhdGgoIi4uIiwgImRhdGEiLCAicHJlZGljdGlvbnMiLCAicHJlZF9iZWhhdmlvdXJfc3MuZnN0IikKCmlmICghZmlsZS5leGlzdHMocHJlZF9nbF9iZWhfcGF0aCkpIHsKICBwcmVkX2dsIDwtIGxvYWRfZGF0YV93aXRoX3ByZWRpY3Rpb25zKCJHcmFuZGVzIExpZ25lcyIpCiAgcHJlZF9nbF9iZWggPC0gcHJlZGljdF9iZWhhdmlvdXIocHJlZF9nbCkKICB3cml0ZV9mc3QocHJlZF9nbF9iZWgsIHByZWRfZ2xfYmVoX3BhdGgpCn0gZWxzZSB7CiAgcHJlZF9nbF9iZWggPC0gcmVhZF9mc3QocHJlZF9nbF9iZWhfcGF0aCkKICBzZXREVChwcmVkX2dsX2JlaCkKfQoKaWYgKCFmaWxlLmV4aXN0cyhwcmVkX3NzX2JlaF9wYXRoKSkgewogIHByZWRfc3MgPC0gbG9hZF9kYXRhX3dpdGhfcHJlZGljdGlvbnMoIlN0ZXBwaW5nIFN0b25lcyIpCiAgcHJlZF9zc19iZWggPC0gcHJlZGljdF9iZWhhdmlvdXIocHJlZF9zcykKICB3cml0ZV9mc3QocHJlZF9zc19iZWgsIHByZWRfc3NfYmVoX3BhdGgpCn0gZWxzZSB7CiAgcHJlZF9zc19iZWggPC0gcmVhZF9mc3QocHJlZF9zc19iZWhfcGF0aCkKICBzZXREVChwcmVkX3NzX2JlaCkKfQpgYGAKCgoKCmBgYHtyfQpwcmVkX2JlaCA8LSByYmluZChwcmVkX2dsX2JlaFssIGNvdXJzZSA6PSAiRnJlbmNoIl0sCiAgICAgICAgICAgICAgICAgIHByZWRfc3NfYmVoWywgY291cnNlIDo9ICJFbmdsaXNoIl0pCgpybShwcmVkX2dsX2JlaCwgcHJlZF9zc19iZWgpCmdjKCkKYGBgCgoKIyBBY3RpdmF0aW9uCgpgYGB7cn0KcF9hY3RfZGlzdCA8LSBnZ3Bsb3QocHJlZF9iZWhbYmV0d2VlbihwcmVkaWN0ZWRfYWN0aXZhdGlvbiwgLTIsIDApXSwgYWVzKHggPSBwcmVkaWN0ZWRfYWN0aXZhdGlvbiwgZmlsbCA9IHByZWRpY3Rpb25fbGFiZWwpKSArCiAgZmFjZXRfZ3JpZChjb3Vyc2UgfiBwcmVkaWN0aW9uX2xhYmVsLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gLi5kZW5zaXR5Li4pLCBiaW53aWR0aCA9IC4wMSkgKwogIGd1aWRlcyhmaWxsID0gIm5vbmUiKSArCiAgbGFicyh4ID0gIlJUIChpbiBzKSIsCiAgICAgICB5ID0gIkRlbnNpdHkiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29uZGl0aW9uX2NvbG91cnMpCgojIHBfYWN0X2Rpc3QKCmdnc2F2ZShwbG90ID0gcF9hY3RfZGlzdCwgZmlsZS5wYXRoKCIuLiIsICJvdXRwdXQiLCAiYWN0aXZhdGlvbl9kaXN0cmlidXRpb24ucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwgd2lkdGggPSA3LjUsIGhlaWdodCA9IDQuNSkKCnJtKHBfYWN0X2Rpc3QpCmBgYAoKCgojIFJlc3BvbnNlIHRpbWUKCkRpc3RyaWJ1dGlvbiBvZiBvYnNlcnZlZCBjb3JyZWN0IFJUICh0cnVuY2F0ZWQgYXQgMjUgc2Vjb25kcyBmb3IgcmVhZGFiaWxpdHkpOgpgYGB7cn0KcF9ydF9kaXN0IDwtIGdncGxvdChwcmVkX2JlaFtjb3JyZWN0ID09IDEgJiBiZXR3ZWVuKHJ0LCAwLCAyNTAwMCldLCBhZXMoeCA9IHJ0LzEwMDApKSArCiAgZmFjZXRfZ3JpZChjb3Vyc2UgfiAuLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gLi5kZW5zaXR5Li4pLCBiaW53aWR0aCA9IC4xKSArCiAgZ3VpZGVzKGZpbGwgPSAibm9uZSIpICsKICBsYWJzKHggPSAiUlQgKGluIHMpIiwKICAgICAgIHkgPSAiRGVuc2l0eSIpCgojIHBfcnRfZGlzdAoKZ2dzYXZlKHBsb3QgPSBwX3J0X2Rpc3QsIGZpbGUucGF0aCgiLi4iLCAib3V0cHV0IiwgInJ0X29ic2VydmVkX2Rpc3RyaWJ1dGlvbi5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDYpCgpybShwX3J0X2Rpc3QpCmBgYAoKIyMgUHJlZGljdGVkIHJlc3BvbnNlIHRpbWUKCiMjIyBEaXN0cmlidXRpb24gb2YgcHJlZGljdGlvbnMKCmBgYHtyfQpwX3J0X3ByZWRfZGlzdCA8LSBnZ3Bsb3QocHJlZF9iZWgsIGFlcyh4ID0gcHJlZGljdGVkX3J0LzEwMDApKSArCiAgZmFjZXRfZ3JpZChjb3Vyc2UgfiAuLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gLi5kZW5zaXR5Li4pLCBiaW53aWR0aCA9IC4xKSArCiAgZ3VpZGVzKGZpbGwgPSAibm9uZSIpICsKICBsYWJzKHggPSAiUHJlZGljdGVkIFJUIChpbiBzKSIsCiAgICAgICB5ID0gIkRlbnNpdHkiKQoKIyBwX3J0X3ByZWRfZGlzdAoKZ2dzYXZlKHBsb3QgPSBwX3J0X3ByZWRfZGlzdCwgZmlsZS5wYXRoKCIuLiIsICJvdXRwdXQiLCAicnRfcHJlZGljdGVkX2Rpc3RyaWJ1dGlvbi5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDYpCgpybShwX3J0X3ByZWRfZGlzdCkKYGBgCgojIyMgUHJlZGljdGVkIHZzIG9ic2VydmVkIHZhbHVlcwoKQ2FsY3VsYXRlIGFic29sdXRlIHByZWRpY3Rpb24gZXJyb3I6CmBgYHtyfQpwcmVkX3J0X2Vycm9yIDwtIChwcmVkX2JlaAogICAgICAgICAgICAgICAgICBbY29ycmVjdCA9PSAxXQogICAgICAgICAgICAgICAgICBbYmV0d2VlbihydCwgMCwgMjUwMDApXQogICAgICAgICAgICAgICAgICBbLCBydF9wcmVkX2Vycm9yIDo9IHByZWRpY3RlZF9ydCAtIHJ0XQogICAgICAgICAgICAgICAgICBbLCBhYnNfcnRfcHJlZF9lcnJvciA6PSBhYnMocnRfcHJlZF9lcnJvcildCikKCnByZWRfcnRfZXJyb3JfYXZnIDwtIHByZWRfcnRfZXJyb3JbLCAuKG1hZSA9IG1lYW4oYWJzX3J0X3ByZWRfZXJyb3IpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZV9zZSA9IHNkKGFic19ydF9wcmVkX2Vycm9yKS8uTiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gLihjb3Vyc2UsIHByZWRpY3Rpb25fbGFiZWwpXQogIApuX29icyA8LSBwcmVkX3J0X2Vycm9yWywgLk4sIGJ5ID0gLihjb3Vyc2UsIHByZWRpY3Rpb25fbGFiZWwpXQpgYGAKCgpgYGB7cn0KIyBwbG90X3JhbmdlIDwtIHJhbmdlKHByZWRfYmVoJHByZWRpY3RlZF9ydC8xMDAwLCBuYS5ybSA9IFRSVUUpCnBsb3RfcmFuZ2UgPC0gYygwLCAxMCkKcGxvdF9icmVha3MgPC0gc2VxKDAsIDEwLCBieSA9IDIpCiMgCiMgcGxvdF9yYW5nZSA8LSBxdWFudGlsZShwcmVkX2JlaCRwcmVkaWN0ZWRfcnQvMTAwMCwgYyguMDA1LCAuOTk1KSkKCnBfcnRfcHJlZF92X29icyA8LSBnZ3Bsb3QocHJlZF9iZWhbY29ycmVjdCA9PSAxXSwgYWVzKHggPSBwcmVkaWN0ZWRfcnQvMTAwMCwgeSA9IHJ0LzEwMDAsIGNvbG91ciA9IHByZWRpY3Rpb25fbGFiZWwpKSArCiAgZmFjZXRfZ3JpZChjb3Vyc2UgfiBwcmVkaWN0aW9uX2xhYmVsKSArCiAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBpbnRlcmNlcHQgPSAwLCBsdHkgPSAzLCBhbHBoYSA9IDAuNzUpICsKICBnZW9tX3BvaW50KGFscGhhID0gLjEsIHNpemUgPSAuMSwgcGNoID0gIi4iKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiB4LCBjb2xvdXIgPSAiYmxhY2siKSArCiAgZ2VvbV9sYWJlbChkYXRhID0gcHJlZF9ydF9lcnJvcl9hdmcsCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IHBhc3RlKCJNQUUgPSIsIGZvcm1hdEMobWFlLzEwMDAsIGRpZ2l0cyA9IDMsIGZsYWcgPSAiIyIpKSksCiAgICAgICAgICAgIHggPSBwbG90X3JhbmdlWzJdLCB5ID0gcGxvdF9yYW5nZVsxXSwKICAgICAgICAgICAgaGp1c3QgPSAxLCBjb2xvdXIgPSAiTkEiLCBzaXplID0gMywKICAgICAgICAgICAgYWxwaGEgPSAuOSwKICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IE5BKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBwcmVkX3J0X2Vycm9yX2F2ZywKICAgICAgICAgICAgYWVzKGxhYmVsID0gcGFzdGUoIk1BRSA9IiwgZm9ybWF0QyhtYWUvMTAwMCwgZGlnaXRzID0gMywgZmxhZyA9ICIjIikpKSwKICAgICAgICAgICAgeCA9IHBsb3RfcmFuZ2VbMl0sIHkgPSBwbG90X3JhbmdlWzFdLAogICAgICAgICAgICBoanVzdCA9IDEsIGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAzKSArCiAgZ2VvbV9sYWJlbChkYXRhID0gbl9vYnMsCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IHBhc3RlKCJuID0iLCBzY2FsZXM6OmNvbW1hKE4pKSksCiAgICAgICAgICAgIHggPSBwbG90X3JhbmdlWzJdLAogICAgICAgICAgICB5ID0gcGxvdF9yYW5nZVsyXSwKICAgICAgICAgICAgaGp1c3QgPSAxLCBjb2xvdXIgPSAiTkEiLCBzaXplID0gMywKICAgICAgICAgICAgYWxwaGEgPSAuOSwKICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IE5BKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBuX29icywKICAgICAgICAgICAgYWVzKGxhYmVsID0gcGFzdGUoIm4gPSIsIHNjYWxlczo6Y29tbWEoTikpKSwKICAgICAgICAgICAgeCA9IHBsb3RfcmFuZ2VbMl0sCiAgICAgICAgICAgIHkgPSBwbG90X3JhbmdlWzJdLAogICAgICAgICAgICBoanVzdCA9IDEsIGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAzKSArCiAgZ3VpZGVzKGNvbG91ciA9ICJub25lIikgKwogIGxhYnMoeCA9ICJQcmVkaWN0ZWQgUlQgKHMpIiwKICAgICAgIHkgPSAiT2JzZXJ2ZWQgUlQgKHMpIikgKwogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSwgeGxpbSA9IHBsb3RfcmFuZ2UsIHlsaW0gPSBwbG90X3JhbmdlKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHBsb3RfYnJlYWtzKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHBsb3RfYnJlYWtzKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb25kaXRpb25fY29sb3VycykKCnBfcnRfcHJlZF92X29icwoKZ2dzYXZlKHBsb3QgPSBwX3J0X3ByZWRfdl9vYnMsIGZpbGUucGF0aCgiLi4iLCAib3V0cHV0IiwgInJ0X3ByZWRpY3RlZF92c19vYnNlcnZlZC5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA0LjUpCgpybShwX3J0X3ByZWRfdl9vYnMpCmBgYAoKIyMjIyBQcmVkaWN0aW9uIGVycm9yCgpEaXN0cmlidXRpb24gb2YgcHJlZGljdGlvbiBlcnJvciAodHJ1bmNhdGVkIHRvIFstNSwgNV0gZm9yIHJlYWRhYmlsaXR5KToKYGBge3J9CnBfcnRfcHJlZF9lcnJvciA8LSBnZ3Bsb3QocHJlZF9ydF9lcnJvciwgYWVzKHggPSBydF9wcmVkX2Vycm9yLzEwMDAsIGZpbGwgPSBwcmVkaWN0aW9uX2xhYmVsKSkgKwogIGZhY2V0X2dyaWQocHJlZGljdGlvbl9sYWJlbCB+IGNvdXJzZSAsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSAuLmRlbnNpdHkuLiksIGJpbndpZHRoID0gLjEpICsKICBndWlkZXMoZmlsbCA9ICJub25lIikgKwogIGxhYnMoeCA9ICJSVCBwcmVkaWN0aW9uIGVycm9yIGluIHMgKHByZWRpY3RlZCAtIG9ic2VydmVkKSIsCiAgICAgICB5ID0gIkRlbnNpdHkiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC01LCA1KSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbmRpdGlvbl9jb2xvdXJzKQoKcF9ydF9wcmVkX2Vycm9yCgpnZ3NhdmUocGxvdCA9IHBfcnRfcHJlZF9lcnJvciwgZmlsZS5wYXRoKCIuLiIsICJvdXRwdXQiLCAicnRfcHJlZGljdGlvbl9lcnJvci5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDUsIGhlaWdodCA9IDcuNSkKCnJtKHBfcnRfcHJlZF9lcnJvcikKYGBgCgojIyMjIEFic29sdXRlIHByZWRpY3Rpb24gZXJyb3IKCmBgYHtyfQpwX3J0X2Fic19wcmVkX2Vycm9yIDwtIGdncGxvdChwcmVkX3J0X2Vycm9yLCBhZXMoeCA9IGFic19ydF9wcmVkX2Vycm9yLzEwMDAsIGZpbGwgPSBwcmVkaWN0aW9uX2xhYmVsKSkgKwogIGZhY2V0X2dyaWQocHJlZGljdGlvbl9sYWJlbCB+IGNvdXJzZSwgc2NhbGVzID0gImZyZWVfeSIpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IC4uZGVuc2l0eS4uKSwgYmlud2lkdGggPSAuMSkgKwogIGd1aWRlcyhmaWxsID0gIm5vbmUiKSArCiAgbGFicyh4ID0gIlJUIHByZWRpY3Rpb24gZXJyb3IgaW4gcyAocHJlZGljdGVkIC0gb2JzZXJ2ZWQpIiwKICAgICAgIHkgPSAiRGVuc2l0eSIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgNSkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb25kaXRpb25fY29sb3VycykKCnBfcnRfYWJzX3ByZWRfZXJyb3IKCmdnc2F2ZShwbG90ID0gcF9ydF9hYnNfcHJlZF9lcnJvciwgZmlsZS5wYXRoKCIuLiIsICJvdXRwdXQiLCAicnRfYWJzb2x1dGVfcHJlZGljdGlvbl9lcnJvci5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDUsIGhlaWdodCA9IDcuNSkKCnJtKHBfcnRfYWJzX3ByZWRfZXJyb3IpCmBgYAoKYGBge3J9CmdncGxvdChwcmVkX3J0X2Vycm9yX2F2ZywgYWVzKHggPSBwcmVkaWN0aW9uX2xhYmVsLCB5ID0gbWFlLzEwMDAsIGNvbG91ciA9IGNvdXJzZSkpICsKICBnZW9tX2JveHBsb3QoZGF0YSA9IHByZWRfcnRfZXJyb3IsCiAgICAgICAgICAgICAgIGFlcyh5ID0gYWJzX3J0X3ByZWRfZXJyb3IvMTAwMCwgZ3JvdXAgPSBpbnRlcmFjdGlvbihjb3Vyc2UsIHByZWRpY3Rpb25fbGFiZWwpKSwKICAgICAgICAgICAgICAgY29sb3VyID0gImdyZXk3MCIsCiAgICAgICAgICAgICAgIHdpZHRoID0gLjI1LAogICAgICAgICAgICAgICBvdXRsaWVyLnNoYXBlID0gTkEsCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAuNSkpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gbWFlLzEwMDAgLSBhZV9zZS8xMDAwLCB5bWF4ID0gbWFlLzEwMDAgKyBhZV9zZS8xMDAwKSwgd2lkdGggPSAwLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gLjUpKSArCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gLjUpKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDIuNSkpICsKICBsYWJzKHggPSAiTWV0aG9kIiwKICAgICAgIHkgPSAiQWJzb2x1dGUgUlQgcHJlZGljdGlvbiBlcnJvciAoaW4gcykiLAogICAgICAgY29sb3VyID0gIkNvdXJzZSIpCmBgYAoKRml0IGEgcmVncmVzc2lvbiBtb2RlbCBvbiBhYnNvbHV0ZSBSVCBwcmVkaWN0aW9uIGVycm9yLgoKCiMjIyMjIEZyZW5jaAoKYGBge3J9Cm1fcnRfcHJlZF9lcnJvcl9nbF9maWxlIDwtIGZpbGUucGF0aCgiLi4iLCAiZGF0YSIsICJtb2RlbF9maXRzIiwgIm1fcnRfcHJlZF9lcnJvcl9HcmFuZGVzX0xpZ25lcy5yZGEiKQoKaWYgKGZpbGUuZXhpc3RzKG1fcnRfcHJlZF9lcnJvcl9nbF9maWxlKSkgewogIGxvYWQobV9ydF9wcmVkX2Vycm9yX2dsX2ZpbGUpCn0gZWxzZSB7CiAgCiAgcHJlZF9nbF9yZWcgPC0gKAogICAgcHJlZF9ydF9lcnJvcgogICAgW2NvdXJzZSA9PSAiRnJlbmNoIl0KICAgIFtzYW1wbGUoLk4sIDFlNildCiAgICBbLCAuKHByZWRpY3Rpb25fbGFiZWwsIGFic19ydF9wcmVkX2Vycm9yLCB1c2VyX2lkLCBmYWN0X2lkKV0KICApCiAgCiAgbV9ydF9wcmVkX2Vycm9yX2dsIDwtIGxtZXIoYWJzX3J0X3ByZWRfZXJyb3IgfiBwcmVkaWN0aW9uX2xhYmVsICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoMSB8IHVzZXJfaWQpICsgKDEgfCBmYWN0X2lkKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gcHJlZF9nbF9yZWcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IGxtZXJDb250cm9sKG9wdGltaXplciA9ImJvYnlxYSIpKQogIAogIHNhdmUobV9ydF9wcmVkX2Vycm9yX2dsLCBmaWxlID0gbV9ydF9wcmVkX2Vycm9yX2dsX2ZpbGUpCn0KCnN1bW1hcnkobV9ydF9wcmVkX2Vycm9yX2dsKQpgYGAKCkNvbXBhcmUgZGlmZmVyZW50IHByZWRpY3Rpb24gdHlwZXMgdG8gZWFjaCBvdGhlcjoKYGBge3J9Cmh0X3J0X2dsIDwtIGdsaHQobV9ydF9wcmVkX2Vycm9yX2dsLCBsaW5mY3QgPSBtY3AocHJlZGljdGlvbl9sYWJlbCA9ICJUdWtleSIpKQpzdW1tYXJ5KGh0X3J0X2dsKQpgYGAKClVzZSBgZW1tZWFuc2AgdG8gZXN0aW1hdGUgdGhlIHN0YW5kYXJkaXNlZCBlZmZlY3Qgc2l6ZSBmb3IgZWFjaCBjb250cmFzdDoKYGBge3J9CmVtbV9ydF9nbCA8LSBlbW1lYW5zKG1fcnRfcHJlZF9lcnJvcl9nbCwgInByZWRpY3Rpb25fbGFiZWwiLCBsbWVyLmRmID0gImFzeW1wdG90aWMiKQojIFNpZ21hIGNhbGN1bGF0aW9uIGZvbGxvd3MgdGhlIGV4YW1wbGUgaW4gdGhlIGVmZl9zaXplIGRvY3VtZW50YXRpb24gKHNlZSBhbHNvIFdlc3RmYWxsIGV0IGFsLiwgMjAxNCkKdmNfcnRfZ2wgPC0gYXMuZGF0YS5mcmFtZShWYXJDb3JyKG1fcnRfcHJlZF9lcnJvcl9nbCkpIApzaWdtYV9ydF9nbCA8LSBzcXJ0KHN1bSh2Y19ydF9nbCR2Y292KSkKZWZmX3NpemVfcnRfZ2wgPC0gZWZmX3NpemUoZW1tX3J0X2dsLCBzaWdtYSA9IHNpZ21hX3J0X2dsLCBlZGYgPSBJbmYpICMgQ2hvaWNlIG9mIGVkZiBkb2VzIG5vdCBhZmZlY3QgZWZmZWN0IHNpemUsIG9ubHkgdGhlIFNFCmVmZl9zaXplX3J0X2dsCmBgYAoKCkluc3BlY3QgdGhlIG1vZGVsJ3MgcmVzaWR1YWxzOgpgYGB7cn0KcXFub3JtKHJlc2lkKG1fcnRfcHJlZF9lcnJvcl9nbCkpCnFxbGluZShyZXNpZChtX3J0X3ByZWRfZXJyb3JfZ2wpLCBjb2wgPSAicmVkIikKYGBgCgpgYGB7cn0KcGxvdChtX3J0X3ByZWRfZXJyb3JfZ2wpCmBgYAoKCiMjIyMjIEVuZ2xpc2gKCmBgYHtyfQptX3J0X3ByZWRfZXJyb3Jfc3NfZmlsZSA8LSBmaWxlLnBhdGgoIi4uIiwgImRhdGEiLCAibW9kZWxfZml0cyIsICJtX3J0X3ByZWRfZXJyb3JfU3RlcHBpbmdfU3RvbmVzLnJkYSIpCgppZiAoZmlsZS5leGlzdHMobV9ydF9wcmVkX2Vycm9yX3NzX2ZpbGUpKSB7CiAgbG9hZChtX3J0X3ByZWRfZXJyb3Jfc3NfZmlsZSkKfSBlbHNlIHsKICAKICBwcmVkX3NzX3JlZyA8LSAoCiAgICBwcmVkX3J0X2Vycm9yCiAgICBbY291cnNlID09ICJFbmdsaXNoIl0KICAgIFtzYW1wbGUoLk4sIDFlNildCiAgICBbLCAuKHByZWRpY3Rpb25fbGFiZWwsIGFic19ydF9wcmVkX2Vycm9yLCB1c2VyX2lkLCBmYWN0X2lkKV0KICApCiAgCiAgbV9ydF9wcmVkX2Vycm9yX3NzIDwtIGxtZXIoYWJzX3J0X3ByZWRfZXJyb3IgfiBwcmVkaWN0aW9uX2xhYmVsICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoMSB8IHVzZXJfaWQpICsgKDEgfCBmYWN0X2lkKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gcHJlZF9zc19yZWcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IGxtZXJDb250cm9sKG9wdGltaXplciA9ImJvYnlxYSIpCiAgKQogIAogIHNhdmUobV9ydF9wcmVkX2Vycm9yX3NzLCBmaWxlID0gbV9ydF9wcmVkX2Vycm9yX3NzX2ZpbGUpCn0KCnN1bW1hcnkobV9ydF9wcmVkX2Vycm9yX3NzKQpgYGAKCkNvbXBhcmUgZGlmZmVyZW50IHByZWRpY3Rpb24gdHlwZXMgdG8gZWFjaCBvdGhlcjoKYGBge3J9Cmh0X3J0X3NzIDwtIGdsaHQobV9ydF9wcmVkX2Vycm9yX3NzLCBsaW5mY3QgPSBtY3AocHJlZGljdGlvbl9sYWJlbCA9ICJUdWtleSIpKQpzdW1tYXJ5KGh0X3J0X3NzKQpgYGAKClVzZSBgZW1tZWFuc2AgdG8gZXN0aW1hdGUgdGhlIHN0YW5kYXJkaXNlZCBlZmZlY3Qgc2l6ZSBmb3IgZWFjaCBjb250cmFzdDoKYGBge3J9CmVtbV9ydF9zcyA8LSBlbW1lYW5zKG1fcnRfcHJlZF9lcnJvcl9zcywgInByZWRpY3Rpb25fbGFiZWwiLCBsbWVyLmRmID0gImFzeW1wdG90aWMiKQojIFNpZ21hIGNhbGN1bGF0aW9uIGZvbGxvd3MgdGhlIGV4YW1wbGUgaW4gdGhlIGVmZl9zaXplIGRvY3VtZW50YXRpb24gKHNlZSBhbHNvIFdlc3RmYWxsIGV0IGFsLiwgMjAxNCkKdmNfcnRfc3MgPC0gYXMuZGF0YS5mcmFtZShWYXJDb3JyKG1fcnRfcHJlZF9lcnJvcl9zcykpIApzaWdtYV9ydF9zcyA8LSBzcXJ0KHN1bSh2Y19ydF9zcyR2Y292KSkKZWZmX3NpemVfcnRfc3MgPC0gZWZmX3NpemUoZW1tX3J0X3NzLCBzaWdtYSA9IHNpZ21hX3J0X3NzLCBlZGYgPSBJbmYpICMgQ2hvaWNlIG9mIGVkZiBkb2VzIG5vdCBhZmZlY3QgZWZmZWN0IHNpemUsIG9ubHkgdGhlIFNFCmVmZl9zaXplX3J0X3NzCmBgYAoKCkluc3BlY3QgdGhlIG1vZGVsJ3MgcmVzaWR1YWxzOgpgYGB7cn0KcXFub3JtKHJlc2lkKG1fcnRfcHJlZF9lcnJvcl9zcykpCnFxbGluZShyZXNpZChtX3J0X3ByZWRfZXJyb3Jfc3MpLCBjb2wgPSAicmVkIikKYGBgCgpgYGB7cn0KcGxvdChtX3J0X3ByZWRfZXJyb3Jfc3MpCmBgYAoKCiMjIyMjIENvbXBhcmlzb24KCmBgYHtyfQpodF9ydF9nbF90aWR5IDwtIGJyb29tOjp0aWR5KGNvbmZpbnQoaHRfcnRfZ2wpKQpodF9ydF9zc190aWR5IDwtIGJyb29tOjp0aWR5KGNvbmZpbnQoaHRfcnRfc3MpKQpzZXREVChodF9ydF9nbF90aWR5KQpzZXREVChodF9ydF9zc190aWR5KQoKaHRfcnRfYm90aF90aWR5IDwtIHJiaW5kKGh0X3J0X2dsX3RpZHlbLCBjb3Vyc2UgOj0gIkZyZW5jaCJdLAogICAgICAgICAgICAgICAgICAgICAgaHRfcnRfc3NfdGlkeVssIGNvdXJzZSA6PSAiRW5nbGlzaCJdKQpgYGAKCmBgYHtyfQpwX3J0X3ByZWRfZXJyb3JfY29tcCA8LSBnZ3Bsb3QoaHRfcnRfYm90aF90aWR5LCBhZXMoeCA9IGNvbnRyYXN0LCB5ID0gZXN0aW1hdGUsIHltaW4gPSBjb25mLmxvdywgeW1heCA9IGNvbmYuaGlnaCwgY29sb3VyID0gY291cnNlKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gIjExIiwgY29sb3VyID0gImdyZXk2MCIpICsKICBnZW9tX2Vycm9yYmFyKHdpZHRoID0gMC4xKSArIAogIGdlb21fcG9pbnQoKSArCiAgbGFicyh4ID0gIkxpbmVhciBoeXBvdGhlc2VzIiwKICAgICAgIHkgPSAiRXN0aW1hdGUiLAogICAgICAgY2FwdGlvbiA9ICJUdWtleSdzIHJhbmdlIHRlc3QuIEVycm9yIGJhcnMgc2hvdyA5NSUgZmFtaWx5LXdpc2UgY29uZmlkZW5jZSBsZXZlbC4iLAogICAgICAgY29sb3VyID0gIkNvdXJzZSIpICsKICBjb29yZF9mbGlwKCkKCnBfcnRfcHJlZF9lcnJvcl9jb21wCgpnZ3NhdmUocGxvdCA9IHBfcnRfcHJlZF9lcnJvcl9jb21wLCBmaWxlLnBhdGgoIi4uIiwgIm91dHB1dCIsICJydF9wcmVkaWN0aW9uX2Vycm9yX2NvbXBhcmlzb25zLnBuZyIpLAogICAgICAgZGV2aWNlID0gInBuZyIsIHdpZHRoID0gNy41LCBoZWlnaHQgPSA1KQoKcm0ocF9ydF9wcmVkX2Vycm9yX2NvbXApCmBgYAoKCiMjIyMjIFN1bW1hcnkgcGxvdAoKYGBge3J9CnByZWRfcnRfZXJyb3JfYXZnWywgcHJlZGljdGlvbl9yYW5rIDo9IGZyYW5rKC1tYWUpLCBieSA9IC4oY291cnNlKV0KCmFubm90YXRpb25fZGZfc3MgPC0gZGF0YS50YWJsZSgKICBjb3Vyc2UgPSByZXAoIkVuZ2xpc2giLCAxMCksCiAgc3RhcnQgPSBjKDEsIDEsIDEsIDEsCiAgICAgICAgICAgIDIsIDIsIDIsCiAgICAgICAgICAgIDMsIDMsCiAgICAgICAgICAgIDQKICApLAogIGVuZCA9IGMoMiwgMywgNCwgNSwKICAgICAgICAgIDMsIDQsIDUsCiAgICAgICAgICA0LCA1LAogICAgICAgICAgNQogICksCiAgeSA9IHNlcShtYXgocHJlZF9ydF9lcnJvcl9hdmckbWFlKSoxLjAxICsgNDUsIG1heChwcmVkX3J0X2Vycm9yX2F2ZyRtYWUpKjEuMDEsIGJ5ID0gLTUpLAogIGxhYmVsID0gYygicCA8IC4wMDEiLCAicCA8IC4wMDEiLCAicCA8IC4wMDEiLCAicCA8IC4wMDEiLAogICAgICAgICAgICAibi5zLiIsICJwIDwgLjAwMSIsICJwIDwgLjAwMSIsCiAgICAgICAgICAgICJuLnMuIiwgInAgPCAuMDEiLAogICAgICAgICAgICAibi5zLiIpCikKCmFubm90YXRpb25fZGZfZ2wgPC0gZGF0YS50YWJsZSgKICBjb3Vyc2UgPSByZXAoIkZyZW5jaCIsIDEwKSwKICBzdGFydCA9IGMoMSwgMSwgMSwgMSwKICAgICAgICAgICAgMiwgMiwgMiwKICAgICAgICAgICAgMywgMywKICAgICAgICAgICAgNAogICksCiAgZW5kID0gYygyLCAzLCA0LCA1LAogICAgICAgICAgMywgNCwgNSwKICAgICAgICAgIDQsIDUsCiAgICAgICAgICA1CiAgKSwKICB5ID0gc2VxKG1heChwcmVkX3J0X2Vycm9yX2F2ZyRtYWUpKjEuMDEgKyA0NSwgbWF4KHByZWRfcnRfZXJyb3JfYXZnJG1hZSkqMS4wMSwgYnkgPSAtNSksCiAgbGFiZWwgPSBjKCJwIDwgLjA1IiwgInAgPCAuMDAxIiwgInAgPCAuMDAxIiwgInAgPCAuMDAxIiwKICAgICAgICAgICAgInAgPCAuMDEiLCAicCA8IC4wMDEiLCAicCA8IC4wMDEiLAogICAgICAgICAgICAicCA8IC4wMDEiLCAicCA8IC4wMDEiLAogICAgICAgICAgICAibi5zLiIpCikKCmFubm90YXRpb25fZGZfcnQgPC0gcmJpbmQoYW5ub3RhdGlvbl9kZl9zcywgYW5ub3RhdGlvbl9kZl9nbCkKYW5ub3RhdGlvbl9kZl9ydFssIGxhYmVsIDo9IGZhY3RvcihsYWJlbCwgbGV2ZWxzID0gYygicCA8IC4wMDEiLCAicCA8IC4wMSIsICJwIDwgLjA1IiwgIm4ucy4iKSldCgpwX3J0X3ByZWRfZXJyb3Jfc3VtbWFyeSA8LSBnZ3Bsb3QocHJlZF9ydF9lcnJvcl9hdmcsIGFlcyh4ID0gcHJlZGljdGlvbl9yYW5rLCB5ID0gbWFlKSkgKwogIGZhY2V0X2dyaWQofiBjb3Vyc2UpICsKICBnZW9tX2xpbmUoZGF0YSA9IGFubm90YXRpb25fZGZfcnQsCiAgICAgICAgICAgIGFlcyh4ID0gMSwgeSA9IDEzMDAsIGx0eSA9IGxhYmVsLCBhbHBoYSA9IGxhYmVsLCBjb2xvdXIgPSBOVUxMKSkgKyAjIER1bW15IGxpbmUgdG8gZ2V0IGxlZ2VuZAogIGdlb21fbGluZShhZXMoY29sb3VyID0gY291cnNlLCBncm91cCA9IGNvdXJzZSkpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gbWFlIC0gYWVfc2UsIHltYXggPSBtYWUgKyBhZV9zZSksIHdpZHRoID0gMCkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGNvdXJzZSwgZ3JvdXAgPSBjb3Vyc2UpKSArCiAgZ2VvbV9sYWJlbChhZXMobGFiZWwgPSBwcmVkaWN0aW9uX2xhYmVsKSwgCiAgICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siLCAKICAgICAgICAgICAgIGFscGhhID0gLjksCiAgICAgICAgICAgICBsYWJlbC5zaXplID0gTkEsIAogICAgICAgICAgICAgbnVkZ2VfeSA9IC0xNSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIkFic29sdXRlIHByZWRpY3Rpb24gZXJyb3I6XG5yZXNwb25zZSB0aW1lIChzKSIsCiAgICAgICBjb2xvdXIgPSAiQ291cnNlIikgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24oYWRkID0gLjc1KSwgYnJlYWtzID0gTlVMTCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hX2Zvcm1hdChiaWcubWFyayA9ICIuIikpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGRhdGFzZXRfY29sb3VycykgKwogIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXMgPSBjKCJwIDwgLjAwMSIgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwIDwgLjAxIiA9IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInAgPCAuMDUiID0gMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibi5zLiIgPSAzKSwKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJQYWlyd2lzZSBjb21wYXJpc29uOiIpICsKICBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzID0gYygicCA8IC4wMDEiID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicCA8IC4wMSIgPSAuNzUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInAgPCAuMDUiID0gLjUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuLnMuIiA9IC4yNSksIAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIlBhaXJ3aXNlIGNvbXBhcmlzb246IikgKwogIGd1aWRlcyhjb2xvdXIgPSAibm9uZSIpICsKICBnZ3NpZ25pZjo6Z2VvbV9zaWduaWYoZGF0YSA9IGFubm90YXRpb25fZGZfcnQsCiAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4bWluID0gc3RhcnQsIHhtYXggPSBlbmQsIGFubm90YXRpb25zID0gIiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgeV9wb3NpdGlvbiA9IHksIGx0eSA9IGxhYmVsLCBhbHBoYSA9IGxhYmVsKSwKICAgICAgICAgICAgICAgICAgICAgICAgdGlwX2xlbmd0aCA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgIG1hbnVhbCA9IFRSVUUpICArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSAicmlnaHQiKQoKcF9ydF9wcmVkX2Vycm9yX3N1bW1hcnkKCmdnc2F2ZShmaWxlLnBhdGgoIi4uIiwgIm91dHB1dCIsICJydF9hYnNvbHV0ZV9wcmVkaWN0aW9uX2Vycm9yX3N1bW1hcnkucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNCkKYGBgCgojIyMjIyBJbXByb3ZlbWVudAoKSG93IGJpZyB3YXMgdGhlIGltcHJvdmVtZW50IGZyb20gd29yc3QgdG8gYmVzdCBwcmVkaWN0aW9uIG1ldGhvZD8KCkZyZW5jaDoKYGBge3J9CiMgQWJzb2x1dGUgY2hhbmdlCmh0X3J0X2dsX3RpZHlbY29udHJhc3QgPT0gIkZhY3QgLSBEZWZhdWx0IiwgZXN0aW1hdGVbMV1dCgojICUgY2hhbmdlCnNjYWxlczo6cGVyY2VudCgKICBodF9ydF9nbF90aWR5W2NvbnRyYXN0ID09ICJGYWN0IC0gRGVmYXVsdCIsIGVzdGltYXRlWzFdXSAvIGZpeGVmKG1fcnRfcHJlZF9lcnJvcl9nbClbWzFdXSwKICBhY2N1cmFjeSA9IC4xKQoKIyBBc3NvY2lhdGVkIHN0YW5kYXJkaXNlZCBlZmZlY3Qgc2l6ZQplZmZfc2l6ZV9ydF9nbF90aWR5IDwtIGJyb29tOjp0aWR5KGVmZl9zaXplX3J0X2dsKSB8PiBhcy5kYXRhLnRhYmxlKCkKZWZmX3NpemVfcnRfZ2xfdGlkeVtjb250cmFzdCA9PSAiRGVmYXVsdCAtIEZhY3QiLCBlc3RpbWF0ZV0KYGBgCgpFbmdsaXNoOgpgYGB7cn0KIyBBYnNvbHV0ZSBjaGFuZ2UKaHRfcnRfc3NfdGlkeVtjb250cmFzdCA9PSAiRmFjdCAmIExlYXJuZXIgLSBEZWZhdWx0IiwgZXN0aW1hdGVbMV1dCgojICUgY2hhbmdlCnNjYWxlczo6cGVyY2VudCgKICBodF9ydF9zc190aWR5W2NvbnRyYXN0ID09ICJGYWN0ICYgTGVhcm5lciAtIERlZmF1bHQiLCBlc3RpbWF0ZVsxXV0gLyBmaXhlZihtX3J0X3ByZWRfZXJyb3Jfc3MpW1sxXV0sCiAgYWNjdXJhY3kgPSAuMSkKCiMgQXNzb2NpYXRlZCBzdGFuZGFyZGlzZWQgZWZmZWN0IHNpemUKZWZmX3NpemVfcnRfc3NfdGlkeSA8LSBicm9vbTo6dGlkeShlZmZfc2l6ZV9ydF9zcykgfD4gYXMuZGF0YS50YWJsZSgpCmVmZl9zaXplX3J0X3NzX3RpZHlbY29udHJhc3QgPT0gIkRlZmF1bHQgLSBGYWN0ICYgTGVhcm5lciIsIGVzdGltYXRlXQpgYGAKCiMjIyMgUk1TRQoKVGhlIG1lYW4gYWJzb2x1dGUgcHJlZGljdGlvbiBlcnJvciBpcyBhIGxpbmVhciBlcnJvciBtZXRyaWMuCldlIGNvdWxkIGFsc28gY2hvb3NlIGEgbm9uLWxpbmVhciBtZXRyaWMgdGhhdCBwZW5hbGlzZXMgbGFyZ2VyIGRldmlhdGlvbnMgbW9yZSBoZWF2aWx5LgpBIGNvbW1vbiBjaG9pY2Ugd291bGQgYmUgUk1TRSAocm9vdCBtZWFuIHNxdWFyZWQgZXJyb3IpLgoKYGBge3J9CnByZWRfcnRfZXJyb3JbLCAuKG1hZSA9IG1lYW4oYWJzX3J0X3ByZWRfZXJyb3IpLAogICAgICAgICAgICAgICAgICBybXNlID0gc3FydChtZWFuKHJ0X3ByZWRfZXJyb3JeMikpKSwKICAgICAgICAgICAgICAgICAgYnkgPSAuKGNvdXJzZSwgcHJlZGljdGlvbl9sYWJlbCldIHw+CiAgZ2dwbG90KGFlcyh4ID0gcHJlZGljdGlvbl9sYWJlbCwgeSA9IHJtc2UsIGdyb3VwID0gY291cnNlLCBjb2xvdXIgPSBjb3Vyc2UpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKQpgYGAKCgoKIyBSZXNwb25zZSBhY2N1cmFjeQoKIyMgUHJlZGljdGVkIHJlc3BvbnNlIGFjY3VyYWN5CgojIyMgRGlzdHJpYnV0aW9uIG9mIHByZWRpY3Rpb25zCgpgYGB7cn0KcF9hY2NfcHJlZF9kaXN0IDwtIGdncGxvdChwcmVkX2JlaCwgYWVzKHggPSBwcmVkaWN0ZWRfYWNjdXJhY3ksIGZpbGwgPSBwcmVkaWN0aW9uX2xhYmVsKSkgKwogICAgZmFjZXRfZ3JpZChjb3Vyc2UgfiBwcmVkaWN0aW9uX2xhYmVsLCBzY2FsZXMgPSAiZnJlZV95IikgKwogICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAuMDEpICsKICAgIGd1aWRlcyhmaWxsID0gIm5vbmUiKSArCiAgICBsYWJzKHggPSAiUHJlZGljdGVkIGFjY3VyYWN5IikgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxLCBieSA9IC4yNSksIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29uZGl0aW9uX2NvbG91cnMpCiAgCnBfYWNjX3ByZWRfZGlzdAoKZ2dzYXZlKHBsb3QgPSBwX2FjY19wcmVkX2Rpc3QsIGZpbGUucGF0aCgiLi4iLCAib3V0cHV0IiwgImFjY19wcmVkaWN0ZWRfZGlzdHJpYnV0aW9uLnBuZyIpLAogICAgICAgICBkZXZpY2UgPSAicG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA3LjUpCgpybShwX2FjY19wcmVkX2Rpc3QpCmBgYAoKCiMjIyBQcmVkaWN0ZWQgdnMgb2JzZXJ2ZWQgdmFsdWVzCgpgYGB7cn0KcGxvdF9kb2RnZSA8LSBmdW5jdGlvbih5LCBkb2RnZSA9IC4xKSB7CiAgcmV0dXJuICh5ICogKDEgKyBkb2RnZSkgLSBkb2RnZS8yKQp9CmBgYAoKCmBgYHtyfQpwX2FjY19wcmVkX3Zfb2JzIDwtIGdncGxvdChwcmVkX2JlaCwgYWVzKHggPSBwcmVkaWN0ZWRfYWNjdXJhY3ksIHkgPSBjb3JyZWN0LCBncm91cCA9IHByZWRpY3Rpb25fbGFiZWwsIGNvbG91ciA9IHByZWRpY3Rpb25fbGFiZWwsIGZpbGwgPSBwcmVkaWN0aW9uX2xhYmVsKSkgKwogICAgZmFjZXRfZ3JpZChjb3Vyc2UgfiBwcmVkaWN0aW9uX2xhYmVsKSArCiAgICBnZW9tX3BvaW50KGFlcyh5ID0gY29ycmVjdCksCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMCwgaGVpZ2h0ID0gLjAyNSwgc2VlZCA9IDEyMyksCiAgICAgICAgICAgICAgIHNpemUgPSAuMDAxLCBwY2ggPSAiLiIsIGFscGhhID0gLjEpICsKICAgIGxhYnMoeCA9ICJQcmVkaWN0ZWQgYWNjdXJhY3kiLAogICAgICAgICB5ID0gIlJlc3BvbnNlIGFjY3VyYWN5IiwKICAgICAgICAgY29sb3VyID0gIlByZWRpY3Rpb24gbWV0aG9kIiwKICAgICAgICAgZmlsbCA9ICJQcmVkaWN0aW9uIG1ldGhvZCIpICsKICBndWlkZXMoY29sb3VyID0gIm5vbmUiLAogICAgICAgICBmaWxsID0gIm5vbmUiKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEsIGJ5ID0gLjI1KSwgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdCgpKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEsIGJ5ID0gLjI1KSwgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdCgpKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGNvbmRpdGlvbl9jb2xvdXJzKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb25kaXRpb25fY29sb3VycykgKwogICAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDEpLCB5bGltID0gYygwLCAxKSwgY2xpcCA9ICJvZmYiKQoKcF9hY2NfcHJlZF92X29icwoKZ2dzYXZlKHBsb3QgPSBwX2FjY19wcmVkX3Zfb2JzLCBmaWxlLnBhdGgoIi4uIiwgIm91dHB1dCIsICJhY2NfcHJlZGljdGVkX3ZzX29ic2VydmVkLnBuZyIpLAogICAgICAgZGV2aWNlID0gInBuZyIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDQuNSkKCnJtKHBfYWNjX3ByZWRfdl9vYnMpCmBgYAoKIyMjIyBQcmVkaWN0aW9uIGVycm9yCgpgYGB7cn0KcHJlZF9hY2NfZXJyb3IgPC0gKHByZWRfYmVoCiAgICAgICAgICAgICAgICAgICBbLCBhY2NfcHJlZF9lcnJvciA6PSBwcmVkaWN0ZWRfYWNjdXJhY3kgLSBjb3JyZWN0XQogICAgICAgICAgICAgICAgICAgWywgYWJzX2FjY19wcmVkX2Vycm9yIDo9IGFicyhhY2NfcHJlZF9lcnJvcildKQoKCnByZWRfYWNjX2Vycm9yX2F2ZyA8LSBwcmVkX2FjY19lcnJvclssIC4obWFlID0gbWVhbihhYnNfYWNjX3ByZWRfZXJyb3IpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlX3NlID0gc2QoYWJzX2FjY19wcmVkX2Vycm9yKS8uTiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAuKGNvdXJzZSwgcHJlZGljdGlvbl9sYWJlbCldCgpuX29icyA8LSBwcmVkX2FjY19lcnJvclssIC5OLCBieSA9IC4oY291cnNlLCBwcmVkaWN0aW9uX2xhYmVsKV0KYGBgCgpEaXN0cmlidXRpb24gb2YgcHJlZGljdGlvbiBlcnJvcjoKYGBge3J9CnBfYWNjX3ByZWRfZXJyb3IgPC0gZ2dwbG90KHByZWRfYWNjX2Vycm9yLCBhZXMoeCA9IGFjY19wcmVkX2Vycm9yLCBmaWxsID0gcHJlZGljdGlvbl9sYWJlbCkpICsKICBmYWNldF9ncmlkKHByZWRpY3Rpb25fbGFiZWwgfiBjb3Vyc2UgLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gLi5kZW5zaXR5Li4pLCBiaW53aWR0aCA9IC4wMSkgKwogIGd1aWRlcyhmaWxsID0gIm5vbmUiKSArCiAgbGFicyh4ID0gIkFjY3VyYWN5IHByZWRpY3Rpb24gZXJyb3IgKHByZWRpY3RlZCAtIG9ic2VydmVkKSIsCiAgICAgICB5ID0gIkRlbnNpdHkiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC0xLCAxKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbmRpdGlvbl9jb2xvdXJzKQoKcF9hY2NfcHJlZF9lcnJvcgoKZ2dzYXZlKHBsb3QgPSBwX2FjY19wcmVkX2Vycm9yLCBmaWxlLnBhdGgoIi4uIiwgIm91dHB1dCIsICJhY2NfcHJlZGljdGlvbl9lcnJvci5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDUsIGhlaWdodCA9IDcuNSkKCnJtKHBfYWNjX3ByZWRfZXJyb3IpCmBgYAoKIyMjIyBBYnNvbHV0ZSBwcmVkaWN0aW9uIGVycm9yCgpgYGB7cn0KcF9hYnNfYWNjX3ByZWRfZXJyb3IgPC0gZ2dwbG90KHByZWRfYWNjX2Vycm9yLCBhZXMoeCA9IGFic19hY2NfcHJlZF9lcnJvciwgZmlsbCA9IHByZWRpY3Rpb25fbGFiZWwpKSArCiAgZmFjZXRfZ3JpZChwcmVkaWN0aW9uX2xhYmVsIH4gY291cnNlICwgc2NhbGVzID0gImZyZWVfeSIpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IC4uZGVuc2l0eS4uKSwgYmlud2lkdGggPSAuMDEpICsKICBndWlkZXMoZmlsbCA9ICJub25lIikgKwogIGxhYnMoeCA9ICJBY2N1cmFjeSBwcmVkaWN0aW9uIGVycm9yIChwcmVkaWN0ZWQgLSBvYnNlcnZlZCkiLAogICAgICAgeSA9ICJEZW5zaXR5IikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCAxKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbmRpdGlvbl9jb2xvdXJzKQoKcF9hYnNfYWNjX3ByZWRfZXJyb3IKCmdnc2F2ZShwbG90ID0gcF9hYnNfYWNjX3ByZWRfZXJyb3IsIGZpbGUucGF0aCgiLi4iLCAib3V0cHV0IiwgImFjY19hYnNvbHV0ZV9wcmVkaWN0aW9uX2Vycm9yLnBuZyIpLAogICAgICAgZGV2aWNlID0gInBuZyIsIHdpZHRoID0gNSwgaGVpZ2h0ID0gNy41KQoKcm0ocF9hYnNfYWNjX3ByZWRfZXJyb3IpCmBgYAoKYGBge3J9CmdncGxvdChwcmVkX2FjY19lcnJvcl9hdmcsIGFlcyh4ID0gcHJlZGljdGlvbl9sYWJlbCwgeSA9IG1hZSwgY29sb3VyID0gY291cnNlKSkgKwogIGdlb21fYm94cGxvdChkYXRhID0gcHJlZF9hY2NfZXJyb3IsCiAgICAgICAgICAgICAgIGFlcyh5ID0gYWJzX2FjY19wcmVkX2Vycm9yLCBncm91cCA9IGludGVyYWN0aW9uKGNvdXJzZSwgcHJlZGljdGlvbl9sYWJlbCkpLAogICAgICAgICAgICAgICBjb2xvdXIgPSAiZ3JleTcwIiwKICAgICAgICAgICAgICAgd2lkdGggPSAuMjUsCiAgICAgICAgICAgICAgIG91dGxpZXIuc2hhcGUgPSBOQSwKICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IC41KSkgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBtYWUgLSBhZV9zZSwgeW1heCA9IG1hZSArIGFlX3NlKSwgd2lkdGggPSAwLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gLjUpKSArCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gLjUpKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDEpKSArCiAgbGFicyh4ID0gIk1ldGhvZCIsCiAgICAgICB5ID0gIkFic29sdXRlIGFjY3VyYWN5IHByZWRpY3Rpb24gZXJyb3IiLAogICAgICAgY29sb3VyID0gIkNvdXJzZSIpCmBgYAoKRml0IGEgcmVncmVzc2lvbiBtb2RlbC4KCiMjIyMjIEZyZW5jaAoKYGBge3J9Cm1fYWNjX3ByZWRfZXJyb3JfZ2xfZmlsZSA8LSBmaWxlLnBhdGgoIi4uIiwgImRhdGEiLCAibW9kZWxfZml0cyIsICJtX2FjY19wcmVkX2Vycm9yX0dyYW5kZXNfTGlnbmVzX25ldy5yZGEiKQoKaWYgKGZpbGUuZXhpc3RzKG1fYWNjX3ByZWRfZXJyb3JfZ2xfZmlsZSkpIHsKICBsb2FkKG1fYWNjX3ByZWRfZXJyb3JfZ2xfZmlsZSkKfSBlbHNlIHsKICAKICBwcmVkX2dsX3JlZyA8LSAoCiAgICBwcmVkX2FjY19lcnJvcgogICAgW2NvdXJzZSA9PSAiRnJlbmNoIl0KICAgIFtzYW1wbGUoLk4sIDFlNildCiAgICBbLCAuKHByZWRpY3Rpb25fbGFiZWwsIGFic19hY2NfcHJlZF9lcnJvciwgdXNlcl9pZCwgZmFjdF9pZCldCiAgKQogIAogIG1fYWNjX3ByZWRfZXJyb3JfZ2wgPC0gbG1lcihhYnNfYWNjX3ByZWRfZXJyb3IgfiBwcmVkaWN0aW9uX2xhYmVsICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoMSB8IHVzZXJfaWQpICsgKDEgfCBmYWN0X2lkKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gcHJlZF9nbF9yZWcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IGxtZXJDb250cm9sKG9wdGltaXplciA9ImJvYnlxYSIpKQogICAgCiAgc2F2ZShtX2FjY19wcmVkX2Vycm9yX2dsLCBmaWxlID0gbV9hY2NfcHJlZF9lcnJvcl9nbF9maWxlKQp9CgpzdW1tYXJ5KG1fYWNjX3ByZWRfZXJyb3JfZ2wpCmBgYAoKQ29tcGFyZSBkaWZmZXJlbnQgcHJlZGljdGlvbiB0eXBlcyB0byBlYWNoIG90aGVyOgpgYGB7cn0KaHRfYWNjX2dsIDwtIGdsaHQobV9hY2NfcHJlZF9lcnJvcl9nbCwgbGluZmN0ID0gbWNwKHByZWRpY3Rpb25fbGFiZWwgPSAiVHVrZXkiKSkKc3VtbWFyeShodF9hY2NfZ2wpCmBgYAoKVXNlIGBlbW1lYW5zYCB0byBlc3RpbWF0ZSB0aGUgc3RhbmRhcmRpc2VkIGVmZmVjdCBzaXplIGZvciBlYWNoIGNvbnRyYXN0OgpgYGB7cn0KZW1tX2FjY19nbCA8LSBlbW1lYW5zKG1fYWNjX3ByZWRfZXJyb3JfZ2wsICJwcmVkaWN0aW9uX2xhYmVsIiwgbG1lci5kZiA9ICJhc3ltcHRvdGljIikKIyBTaWdtYSBjYWxjdWxhdGlvbiBmb2xsb3dzIHRoZSBleGFtcGxlIGluIHRoZSBlZmZfc2l6ZSBkb2N1bWVudGF0aW9uIChzZWUgYWxzbyBXZXN0ZmFsbCBldCBhbC4sIDIwMTQpCnZjX2FjY19nbCA8LSBhcy5kYXRhLmZyYW1lKFZhckNvcnIobV9hY2NfcHJlZF9lcnJvcl9nbCkpIApzaWdtYV9hY2NfZ2wgPC0gc3FydChzdW0odmNfYWNjX2dsJHZjb3YpKQplZmZfc2l6ZV9hY2NfZ2wgPC0gZWZmX3NpemUoZW1tX2FjY19nbCwgc2lnbWEgPSBzaWdtYV9hY2NfZ2wsIGVkZiA9IEluZikgIyBDaG9pY2Ugb2YgZWRmIGRvZXMgbm90IGFmZmVjdCBlZmZlY3Qgc2l6ZSwgb25seSB0aGUgU0UKZWZmX3NpemVfYWNjX2dsCmBgYAoKSW5zcGVjdCB0aGUgbW9kZWwncyByZXNpZHVhbHM6CmBgYHtyfQpxcW5vcm0ocmVzaWQobV9hY2NfcHJlZF9lcnJvcl9nbCkpCnFxbGluZShyZXNpZChtX2FjY19wcmVkX2Vycm9yX2dsKSwgY29sID0gInJlZCIpCmBgYAoKYGBge3J9CnBsb3QobV9hY2NfcHJlZF9lcnJvcl9nbCkKYGBgCgojIyMjIyBFbmdsaXNoCgpgYGB7cn0KbV9hY2NfcHJlZF9lcnJvcl9zc19maWxlIDwtIGZpbGUucGF0aCgiLi4iLCAiZGF0YSIsICJtb2RlbF9maXRzIiwgIm1fYWNjX3ByZWRfZXJyb3JfU3RlcHBpbmdfU3RvbmVzX25ldy5yZGEiKQoKaWYgKGZpbGUuZXhpc3RzKG1fYWNjX3ByZWRfZXJyb3Jfc3NfZmlsZSkpIHsKICBsb2FkKG1fYWNjX3ByZWRfZXJyb3Jfc3NfZmlsZSkKfSBlbHNlIHsKICAKICBwcmVkX3NzX3JlZyA8LSAoCiAgICBwcmVkX2FjY19lcnJvcgogICAgW2NvdXJzZSA9PSAiRW5nbGlzaCJdCiAgICBbc2FtcGxlKC5OLCAxZTYpXQogICAgWywgLihwcmVkaWN0aW9uX2xhYmVsLCBhYnNfYWNjX3ByZWRfZXJyb3IsIHVzZXJfaWQsIGZhY3RfaWQpXQogICkKICAKICBtX2FjY19wcmVkX2Vycm9yX3NzIDwtIGxtZXIoYWJzX2FjY19wcmVkX2Vycm9yIH4gcHJlZGljdGlvbl9sYWJlbCArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKDEgfCB1c2VyX2lkKSArICgxIHwgZmFjdF9pZCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHByZWRfc3NfcmVnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBsbWVyQ29udHJvbChvcHRpbWl6ZXIgPSJib2J5cWEiKQogICkKICAKICBzYXZlKG1fYWNjX3ByZWRfZXJyb3Jfc3MsIGZpbGUgPSBtX2FjY19wcmVkX2Vycm9yX3NzX2ZpbGUpCn0KCnN1bW1hcnkobV9hY2NfcHJlZF9lcnJvcl9zcykKYGBgCgpDb21wYXJlIGRpZmZlcmVudCBwcmVkaWN0aW9uIHR5cGVzIHRvIGVhY2ggb3RoZXI6CmBgYHtyfQpodF9hY2Nfc3MgPC0gZ2xodChtX2FjY19wcmVkX2Vycm9yX3NzLCBsaW5mY3QgPSBtY3AocHJlZGljdGlvbl9sYWJlbCA9ICJUdWtleSIpKQpzdW1tYXJ5KGh0X2FjY19zcykKYGBgCgpVc2UgYGVtbWVhbnNgIHRvIGVzdGltYXRlIHRoZSBzdGFuZGFyZGlzZWQgZWZmZWN0IHNpemUgZm9yIGVhY2ggY29udHJhc3Q6CmBgYHtyfQplbW1fYWNjX3NzIDwtIGVtbWVhbnMobV9hY2NfcHJlZF9lcnJvcl9zcywgInByZWRpY3Rpb25fbGFiZWwiLCBsbWVyLmRmID0gImFzeW1wdG90aWMiKQojIFNpZ21hIGNhbGN1bGF0aW9uIGZvbGxvd3MgdGhlIGV4YW1wbGUgaW4gdGhlIGVmZl9zaXplIGRvY3VtZW50YXRpb24gKHNlZSBhbHNvIFdlc3RmYWxsIGV0IGFsLiwgMjAxNCkKdmNfYWNjX3NzIDwtIGFzLmRhdGEuZnJhbWUoVmFyQ29ycihtX2FjY19wcmVkX2Vycm9yX3NzKSkgCnNpZ21hX2FjY19zcyA8LSBzcXJ0KHN1bSh2Y19hY2Nfc3MkdmNvdikpCmVmZl9zaXplX2FjY19zcyA8LSBlZmZfc2l6ZShlbW1fYWNjX3NzLCBzaWdtYSA9IHNpZ21hX2FjY19zcywgZWRmID0gSW5mKSAjIENob2ljZSBvZiBlZGYgZG9lcyBub3QgYWZmZWN0IGVmZmVjdCBzaXplLCBvbmx5IHRoZSBTRQplZmZfc2l6ZV9hY2Nfc3MKYGBgCgpJbnNwZWN0IHRoZSBtb2RlbCdzIHJlc2lkdWFsczoKYGBge3J9CnFxbm9ybShyZXNpZChtX2FjY19wcmVkX2Vycm9yX3NzKSkKcXFsaW5lKHJlc2lkKG1fYWNjX3ByZWRfZXJyb3Jfc3MpLCBjb2wgPSAicmVkIikKYGBgCgpgYGB7cn0KcGxvdChtX2FjY19wcmVkX2Vycm9yX3NzKQpgYGAKCiMjIyMjIENvbXBhcmlzb24KCmBgYHtyfQpodF9hY2NfZ2xfdGlkeSA8LSBicm9vbTo6dGlkeShjb25maW50KGh0X2FjY19nbCkpCmh0X2FjY19zc190aWR5IDwtIGJyb29tOjp0aWR5KGNvbmZpbnQoaHRfYWNjX3NzKSkKc2V0RFQoaHRfYWNjX2dsX3RpZHkpCnNldERUKGh0X2FjY19zc190aWR5KQoKaHRfYWNjX2JvdGhfdGlkeSA8LSByYmluZChodF9hY2NfZ2xfdGlkeVssIGNvdXJzZSA6PSAiRnJlbmNoIl0sCiAgICAgICAgICAgICAgICAgICAgICBodF9hY2Nfc3NfdGlkeVssIGNvdXJzZSA6PSAiRW5nbGlzaCJdKQpgYGAKCmBgYHtyfQpwX2FjY19wcmVkX2Vycm9yX2NvbXAgPC0gZ2dwbG90KGh0X2FjY19ib3RoX3RpZHksIGFlcyh4ID0gY29udHJhc3QsIHkgPSBlc3RpbWF0ZSwgeW1pbiA9IGNvbmYubG93LCB5bWF4ID0gY29uZi5oaWdoLCBjb2xvdXIgPSBjb3Vyc2UpKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiMTEiLCBjb2xvdXIgPSAiZ3JleTYwIikgKwogIGdlb21fZXJyb3JiYXIod2lkdGggPSAwLjEpICsgCiAgZ2VvbV9wb2ludCgpICsKICBsYWJzKHggPSAiTGluZWFyIGh5cG90aGVzZXMiLAogICAgICAgeSA9ICJFc3RpbWF0ZSIsCiAgICAgICBjYXB0aW9uID0gIlR1a2V5J3MgcmFuZ2UgdGVzdC4gRXJyb3IgYmFycyBzaG93IDk1JSBmYW1pbHktd2lzZSBjb25maWRlbmNlIGxldmVsLiIsCiAgICAgICBjb2xvdXIgPSAiQ291cnNlIikgKwogIGNvb3JkX2ZsaXAoKQoKcF9hY2NfcHJlZF9lcnJvcl9jb21wCgpnZ3NhdmUocGxvdCA9IHBfYWNjX3ByZWRfZXJyb3JfY29tcCwgZmlsZS5wYXRoKCIuLiIsICJvdXRwdXQiLCAiYWNjX3ByZWRpY3Rpb25fZXJyb3JfY29tcGFyaXNvbnMucG5nIiksCiAgICAgICBkZXZpY2UgPSAicG5nIiwgd2lkdGggPSA3LjUsIGhlaWdodCA9IDUpCgpybShwX2FjY19wcmVkX2Vycm9yX2NvbXApCmBgYAoKCiMjIyMjIFN1bW1hcnkgcGxvdAoKYGBge3J9CnByZWRfYWNjX2Vycm9yX2F2Z1ssIHByZWRpY3Rpb25fcmFuayA6PSBmcmFuaygtbWFlKSwgYnkgPSAuKGNvdXJzZSldCgphbm5vdGF0aW9uX2RmX3NzIDwtIGRhdGEudGFibGUoCiAgY291cnNlID0gcmVwKCJFbmdsaXNoIiwgMTApLAogIHN0YXJ0ID0gYygxLCAxLCAxLCAxLAogICAgICAgICAgICAyLCAyLCAyLAogICAgICAgICAgICAzLCAzLAogICAgICAgICAgICA0CiAgKSwKICBlbmQgPSBjKDIsIDMsIDQsIDUsCiAgICAgICAgICAzLCA0LCA1LAogICAgICAgICAgNCwgNSwKICAgICAgICAgIDUKICApLAogIHkgPSBzZXEobWF4KHByZWRfYWNjX2Vycm9yX2F2ZyRtYWUpKjEuMDEgKyAuMDIyNSwgbWF4KHByZWRfYWNjX2Vycm9yX2F2ZyRtYWUpKjEuMDEsIGJ5ID0gLS4wMDI1KSwKICBsYWJlbCA9IGMoInAgPCAuMDAxIiwgInAgPCAuMDAxIiwgInAgPCAuMDAxIiwgInAgPCAuMDAxIiwKICAgICAgICAgICAgInAgPCAuMDAxIiwgInAgPCAuMDAxIiwgInAgPCAuMDAxIiwKICAgICAgICAgICAgInAgPCAuMDAxIiwgInAgPCAuMDAxIiwKICAgICAgICAgICAgInAgPCAuMDAxIikKKQoKYW5ub3RhdGlvbl9kZl9nbCA8LSBkYXRhLnRhYmxlKAogIGNvdXJzZSA9IHJlcCgiRnJlbmNoIiwgMTApLAogIHN0YXJ0ID0gYygxLCAxLCAxLCAxLAogICAgICAgICAgICAyLCAyLCAyLAogICAgICAgICAgICAzLCAzLAogICAgICAgICAgICA0CiAgKSwKICBlbmQgPSBjKDIsIDMsIDQsIDUsCiAgICAgICAgICAzLCA0LCA1LAogICAgICAgICAgNCwgNSwKICAgICAgICAgIDUKICApLAogIHkgPSBzZXEobWF4KHByZWRfYWNjX2Vycm9yX2F2ZyRtYWUpKjEuMDEgKyAuMDIyNSwgbWF4KHByZWRfYWNjX2Vycm9yX2F2ZyRtYWUpKjEuMDEsIGJ5ID0gLS4wMDI1KSwKICBsYWJlbCA9IGMoInAgPCAuMDAxIiwgInAgPCAuMDAxIiwgInAgPCAuMDAxIiwgInAgPCAuMDAxIiwKICAgICAgICAgICAgInAgPCAuMDAxIiwgInAgPCAuMDAxIiwgInAgPCAuMDAxIiwKICAgICAgICAgICAgInAgPCAuMDAxIiwgInAgPCAuMDAxIiwKICAgICAgICAgICAgInAgPCAuMDAxIikKKQoKYW5ub3RhdGlvbl9kZl9hY2MgPC0gcmJpbmQoYW5ub3RhdGlvbl9kZl9zcywgYW5ub3RhdGlvbl9kZl9nbCkKYW5ub3RhdGlvbl9kZl9hY2NbLCBsYWJlbCA6PSBmYWN0b3IobGFiZWwsIGxldmVscyA9IGMoInAgPCAuMDAxIiwgInAgPCAuMDEiLCAicCA8IC4wNSIsICJuLnMuIikpXQoKcF9hY2NfcHJlZF9lcnJvcl9zdW1tYXJ5IDwtIGdncGxvdChwcmVkX2FjY19lcnJvcl9hdmcsIGFlcyh4ID0gcHJlZGljdGlvbl9yYW5rLCB5ID0gbWFlKSkgKwogIGZhY2V0X2dyaWQofiBjb3Vyc2UpICsKICBnZW9tX2xpbmUoZGF0YSA9IGFubm90YXRpb25fZGZfYWNjLAogICAgICAgICAgICBhZXMoeCA9IDEsIHkgPSAuNDUsIGx0eSA9IGxhYmVsLCBhbHBoYSA9IGxhYmVsLCBjb2xvdXIgPSBOVUxMKSkgKyAjIER1bW15IGxpbmUgdG8gZ2V0IGxlZ2VuZAogIGdlb21fbGluZShhZXMoY29sb3VyID0gY291cnNlLCBncm91cCA9IGNvdXJzZSkpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gbWFlIC0gYWVfc2UsIHltYXggPSBtYWUgKyBhZV9zZSksIHdpZHRoID0gMCkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGNvdXJzZSwgZ3JvdXAgPSBjb3Vyc2UpKSArCiAgZ2VvbV9sYWJlbChhZXMobGFiZWwgPSBwcmVkaWN0aW9uX2xhYmVsKSwgCiAgICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siLCAKICAgICAgICAgICAgIGFscGhhID0gLjksCiAgICAgICAgICAgICBsYWJlbC5zaXplID0gTkEsIAogICAgICAgICAgICAgbnVkZ2VfeSA9IC0uMDA3KSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiQWJzb2x1dGUgcHJlZGljdGlvbiBlcnJvcjpcbnJlc3BvbnNlIGFjY3VyYWN5IiwKICAgICAgIGNvbG91ciA9ICJDb3Vyc2UiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihhZGQgPSAuNzUpLCBicmVha3MgPSBOVUxMKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBkYXRhc2V0X2NvbG91cnMpICsKICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygicCA8IC4wMDEiID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicCA8IC4wMSIgPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwIDwgLjA1IiA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm4ucy4iID0gMyksCiAgICAgICAgICAgICAgICAgICAgICAgIGRyb3AgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJQYWlyd2lzZSBjb21wYXJpc29uOiIpICsKICBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzID0gYygicCA8IC4wMDEiID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicCA8IC4wMSIgPSAuNzUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInAgPCAuMDUiID0gLjUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuLnMuIiA9IC4yNSksIAogICAgICAgICAgICAgICAgICAgICBkcm9wID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiUGFpcndpc2UgY29tcGFyaXNvbjoiKSArCiAgZ3VpZGVzKGNvbG91ciA9ICJub25lIikgKwogIGdnc2lnbmlmOjpnZW9tX3NpZ25pZihkYXRhID0gYW5ub3RhdGlvbl9kZl9hY2MsCiAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4bWluID0gc3RhcnQsIHhtYXggPSBlbmQsIGFubm90YXRpb25zID0gIiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgeV9wb3NpdGlvbiA9IHksIGx0eSA9IGxhYmVsLCBhbHBoYSA9IGxhYmVsKSwKICAgICAgICAgICAgICAgICAgICAgICAgdGlwX2xlbmd0aCA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgIG1hbnVhbCA9IFRSVUUpICArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSAicmlnaHQiKQoKcF9hY2NfcHJlZF9lcnJvcl9zdW1tYXJ5CgpnZ3NhdmUoZmlsZS5wYXRoKCIuLiIsICJvdXRwdXQiLCAiYWNjX2Fic29sdXRlX3ByZWRpY3Rpb25fZXJyb3Jfc3VtbWFyeS5wbmciKSwKICAgICAgIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA0KQpgYGAKCiMjIyMjIEltcHJvdmVtZW50CgpIb3cgYmlnIHdhcyB0aGUgaW1wcm92ZW1lbnQgZnJvbSB3b3JzdCB0byBiZXN0IHByZWRpY3Rpb24gbWV0aG9kPwoKRnJlbmNoOgpgYGB7cn0KIyBBYnNvbHV0ZSBjaGFuZ2UKaHRfYWNjX2dsX3RpZHlbY29udHJhc3QgPT0gIkZhY3QgJiBMZWFybmVyIC0gRGVmYXVsdCIsIGVzdGltYXRlWzFdXQoKIyAlIGNoYW5nZQpzY2FsZXM6OnBlcmNlbnQoCiAgaHRfYWNjX2dsX3RpZHlbY29udHJhc3QgPT0gIkZhY3QgJiBMZWFybmVyIC0gRGVmYXVsdCIsIGVzdGltYXRlWzFdXSAvIGZpeGVmKG1fYWNjX3ByZWRfZXJyb3JfZ2wpW1sxXV0sCiAgYWNjdXJhY3kgPSAuMSkKCiMgQXNzb2NpYXRlZCBzdGFuZGFyZGlzZWQgZWZmZWN0IHNpemUKZWZmX3NpemVfYWNjX2dsX3RpZHkgPC0gYnJvb206OnRpZHkoZWZmX3NpemVfYWNjX2dsKSB8PiBhcy5kYXRhLnRhYmxlKCkKZWZmX3NpemVfYWNjX2dsX3RpZHlbY29udHJhc3QgPT0gIkRlZmF1bHQgLSBGYWN0ICYgTGVhcm5lciIsIGVzdGltYXRlXQpgYGAKCkVuZ2xpc2g6CmBgYHtyfQojIEFic29sdXRlIGNoYW5nZQpodF9hY2Nfc3NfdGlkeVtjb250cmFzdCA9PSAiRmFjdCAmIExlYXJuZXIgLSBEZWZhdWx0IiwgZXN0aW1hdGVbMV1dCgojICUgY2hhbmdlCnNjYWxlczo6cGVyY2VudCgKICBodF9hY2Nfc3NfdGlkeVtjb250cmFzdCA9PSAiRmFjdCAmIExlYXJuZXIgLSBEZWZhdWx0IiwgZXN0aW1hdGVbMV1dIC8gZml4ZWYobV9hY2NfcHJlZF9lcnJvcl9zcylbWzFdXSwKICBhY2N1cmFjeSA9IC4xKQoKIyBBc3NvY2lhdGVkIHN0YW5kYXJkaXNlZCBlZmZlY3Qgc2l6ZQplZmZfc2l6ZV9hY2Nfc3NfdGlkeSA8LSBicm9vbTo6dGlkeShlZmZfc2l6ZV9hY2Nfc3MpIHw+IGFzLmRhdGEudGFibGUoKQplZmZfc2l6ZV9hY2Nfc3NfdGlkeVtjb250cmFzdCA9PSAiRGVmYXVsdCAtIEZhY3QgJiBMZWFybmVyIiwgZXN0aW1hdGVdCmBgYAoKCiMgQ29tYmluZWQgcGxvdAoKYGBge3J9CihwX2FjY19wcmVkX2Vycm9yX3N1bW1hcnkgKyBwX3J0X3ByZWRfZXJyb3Jfc3VtbWFyeSkgKyAKICBwbG90X2xheW91dChuY29sID0gMSwgZ3VpZGVzID0gImNvbGxlY3QiKSArCiAgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSAiQSIpICYKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmdnc2F2ZShmaWxlLnBhdGgoIi4uIiwgIm91dHB1dCIsICJiZWhfYWJzb2x1dGVfcHJlZGljdGlvbl9lcnJvcl9zdW1tYXJ5LnBuZyIpLAogICAgICAgZGV2aWNlID0gInBuZyIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgpCmBgYAoKCiMgU2Vzc2lvbiBpbmZvCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGA=